diff options
author | Daniel Mueller <deso@posteo.net> | 2020-01-02 08:32:06 -0800 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2020-01-02 08:32:06 -0800 |
commit | fd091b04316db9dc5fafadbd6bdbe60b127408a9 (patch) | |
tree | f202270f7ae5cedc513be03833a26148d9b5e219 | |
parent | 8161cdb26f98e65b39c603ddf7a614cc87c77a1c (diff) | |
download | nitrocli-fd091b04316db9dc5fafadbd6bdbe60b127408a9.tar.gz nitrocli-fd091b04316db9dc5fafadbd6bdbe60b127408a9.tar.bz2 |
Update nitrokey crate to 0.4.0
This change finally updates the version of the nitrokey crate that we
consume to 0.4.0. Along with that we update rand_core, one of its
dependencies, to 0.5.1. Further more we add cfg-if in version 0.1.10 and
getrandom in version 0.1.13, both of which are now new (non-development)
dependencies.
Import subrepo nitrokey/:nitrokey at e81057037e9b4f370b64c0a030a725bc6bdfb870
Import subrepo cfg-if/:cfg-if at 4484a6faf816ff8058088ad857b0c6bb2f4b02b2
Import subrepo getrandom/:getrandom at d661aa7e1b8cc80b47dabe3d2135b3b47d2858af
Import subrepo rand/:rand at d877ed528248b52d947e0484364a4e1ae59ca502
241 files changed, 12290 insertions, 6682 deletions
diff --git a/cfg-if/.github/workflows/main.yml b/cfg-if/.github/workflows/main.yml new file mode 100644 index 0000000..411ffe7 --- /dev/null +++ b/cfg-if/.github/workflows/main.yml @@ -0,0 +1,53 @@ +name: CI +on: [push, pull_request] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + rust: [stable, beta, nightly] + steps: + - uses: actions/checkout@master + - name: Install Rust Stable + run: | + rustup self update + rustup update ${{ matrix.rust }} + rustup default ${{ matrix.rust }} + rustc -vV + - name: Run tests + run: cargo test + + rustfmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Install Rust Stable + run: | + rustup update stable + rustup default stable + rustup component add rustfmt + - name: Run rustfmt + run: cargo fmt -- --check + + publish_docs: + name: Publish Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Install Rust Stable + run: | + rustup update stable + rustup default stable + - name: Build documentation + run: cargo doc --no-deps + - name: Publish documentation + run: | + cd target/doc + git init + git add . + git -c user.name='ci' -c user.email='ci' commit -m init + git push -f -q https://git:${{ secrets.github_token }}@github.com/${{ github.repository }} HEAD:gh-pages + if: github.event_name == 'push' && github.event.ref == 'refs/heads/master' diff --git a/cfg-if/.gitignore b/cfg-if/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/cfg-if/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/cfg-if/Cargo.toml b/cfg-if/Cargo.toml new file mode 100644 index 0000000..01bd1a7 --- /dev/null +++ b/cfg-if/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "cfg-if" +version = "0.1.10" +authors = ["Alex Crichton <alex@alexcrichton.com>"] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/alexcrichton/cfg-if" +homepage = "https://github.com/alexcrichton/cfg-if" +documentation = "https://docs.rs/cfg-if" +description = """ +A macro to ergonomically define an item depending on a large number of #[cfg] +parameters. Structured like an if-else chain, the first matching branch is the +item that gets emitted. +""" +edition = "2018" + +[badges] +travis-ci = { repository = "alexcrichton/cfg-if" } + +[dependencies] +core = { version = "1.0.0", optional = true, package = 'rustc-std-workspace-core' } +compiler_builtins = { version = '0.1.2', optional = true } + +[features] +rustc-dep-of-std = ['core', 'compiler_builtins'] diff --git a/cfg-if/LICENSE-APACHE b/cfg-if/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/cfg-if/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/cfg-if/LICENSE-MIT b/cfg-if/LICENSE-MIT new file mode 100644 index 0000000..39e0ed6 --- /dev/null +++ b/cfg-if/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/cfg-if/README.md b/cfg-if/README.md new file mode 100644 index 0000000..50b5e3b --- /dev/null +++ b/cfg-if/README.md @@ -0,0 +1,47 @@ +# cfg-if + +[Documentation](https://docs.rs/cfg-if) + +A macro to ergonomically define an item depending on a large number of #[cfg] +parameters. Structured like an if-else chain, the first matching branch is the +item that gets emitted. + +```toml +[dependencies] +cfg-if = "0.1" +``` + +## Example + +```rust +cfg_if::cfg_if! { + if #[cfg(unix)] { + fn foo() { /* unix specific functionality */ } + } else if #[cfg(target_pointer_width = "32")] { + fn foo() { /* non-unix, 32-bit functionality */ } + } else { + fn foo() { /* fallback implementation */ } + } +} + +fn main() { + foo(); +} +``` + +# License + +This project is licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in `cfg-if` by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/cfg-if/src/lib.rs b/cfg-if/src/lib.rs new file mode 100644 index 0000000..6c5058d --- /dev/null +++ b/cfg-if/src/lib.rs @@ -0,0 +1,176 @@ +//! A macro for defining `#[cfg]` if-else statements. +//! +//! The macro provided by this crate, `cfg_if`, is similar to the `if/elif` C +//! preprocessor macro by allowing definition of a cascade of `#[cfg]` cases, +//! emitting the implementation which matches first. +//! +//! This allows you to conveniently provide a long list `#[cfg]`'d blocks of code +//! without having to rewrite each clause multiple times. +//! +//! # Example +//! +//! ``` +//! cfg_if::cfg_if! { +//! if #[cfg(unix)] { +//! fn foo() { /* unix specific functionality */ } +//! } else if #[cfg(target_pointer_width = "32")] { +//! fn foo() { /* non-unix, 32-bit functionality */ } +//! } else { +//! fn foo() { /* fallback implementation */ } +//! } +//! } +//! +//! # fn main() {} +//! ``` + +#![no_std] +#![doc(html_root_url = "https://docs.rs/cfg-if")] +#![deny(missing_docs)] +#![cfg_attr(test, deny(warnings))] + +/// The main macro provided by this crate. See crate documentation for more +/// information. +#[macro_export] +macro_rules! cfg_if { + // match if/else chains with a final `else` + ($( + if #[cfg($($meta:meta),*)] { $($tokens:tt)* } + ) else * else { + $($tokens2:tt)* + }) => { + $crate::cfg_if! { + @__items + () ; + $( ( ($($meta),*) ($($tokens)*) ), )* + ( () ($($tokens2)*) ), + } + }; + + // match if/else chains lacking a final `else` + ( + if #[cfg($($i_met:meta),*)] { $($i_tokens:tt)* } + $( + else if #[cfg($($e_met:meta),*)] { $($e_tokens:tt)* } + )* + ) => { + $crate::cfg_if! { + @__items + () ; + ( ($($i_met),*) ($($i_tokens)*) ), + $( ( ($($e_met),*) ($($e_tokens)*) ), )* + ( () () ), + } + }; + + // Internal and recursive macro to emit all the items + // + // Collects all the negated cfgs in a list at the beginning and after the + // semicolon is all the remaining items + (@__items ($($not:meta,)*) ; ) => {}; + (@__items ($($not:meta,)*) ; ( ($($m:meta),*) ($($tokens:tt)*) ), $($rest:tt)*) => { + // Emit all items within one block, applying an appropriate #[cfg]. The + // #[cfg] will require all `$m` matchers specified and must also negate + // all previous matchers. + #[cfg(all($($m,)* not(any($($not),*))))] $crate::cfg_if! { @__identity $($tokens)* } + + // Recurse to emit all other items in `$rest`, and when we do so add all + // our `$m` matchers to the list of `$not` matchers as future emissions + // will have to negate everything we just matched as well. + $crate::cfg_if! { @__items ($($not,)* $($m,)*) ; $($rest)* } + }; + + // Internal macro to make __apply work out right for different match types, + // because of how macros matching/expand stuff. + (@__identity $($tokens:tt)*) => { + $($tokens)* + }; +} + +#[cfg(test)] +mod tests { + cfg_if! { + if #[cfg(test)] { + use core::option::Option as Option2; + fn works1() -> Option2<u32> { Some(1) } + } else { + fn works1() -> Option<u32> { None } + } + } + + cfg_if! { + if #[cfg(foo)] { + fn works2() -> bool { false } + } else if #[cfg(test)] { + fn works2() -> bool { true } + } else { + fn works2() -> bool { false } + } + } + + cfg_if! { + if #[cfg(foo)] { + fn works3() -> bool { false } + } else { + fn works3() -> bool { true } + } + } + + cfg_if! { + if #[cfg(test)] { + use core::option::Option as Option3; + fn works4() -> Option3<u32> { Some(1) } + } + } + + cfg_if! { + if #[cfg(foo)] { + fn works5() -> bool { false } + } else if #[cfg(test)] { + fn works5() -> bool { true } + } + } + + #[test] + fn it_works() { + assert!(works1().is_some()); + assert!(works2()); + assert!(works3()); + assert!(works4().is_some()); + assert!(works5()); + } + + #[test] + #[allow(clippy::assertions_on_constants)] + fn test_usage_within_a_function() { + cfg_if! {if #[cfg(debug_assertions)] { + // we want to put more than one thing here to make sure that they + // all get configured properly. + assert!(cfg!(debug_assertions)); + assert_eq!(4, 2+2); + } else { + assert!(works1().is_some()); + assert_eq!(10, 5+5); + }} + } + + trait Trait { + fn blah(&self); + } + + #[allow(dead_code)] + struct Struct; + + impl Trait for Struct { + cfg_if! { + if #[cfg(feature = "blah")] { + fn blah(&self) { + unimplemented!(); + } + } else { + fn blah(&self) { + unimplemented!(); + } + } + } + } +} diff --git a/cfg-if/tests/xcrate.rs b/cfg-if/tests/xcrate.rs new file mode 100644 index 0000000..e7b4a36 --- /dev/null +++ b/cfg-if/tests/xcrate.rs @@ -0,0 +1,14 @@ +cfg_if::cfg_if! { + if #[cfg(foo)] { + fn works() -> bool { false } + } else if #[cfg(test)] { + fn works() -> bool { true } + } else { + fn works() -> bool { false } + } +} + +#[test] +fn smoke() { + assert!(works()); +} diff --git a/getrandom/.cargo/config b/getrandom/.cargo/config new file mode 100644 index 0000000..291ce5e --- /dev/null +++ b/getrandom/.cargo/config @@ -0,0 +1,5 @@ +[target.wasm32-unknown-unknown] +runner = 'wasm-bindgen-test-runner' + +[target.wasm32-wasi] +runner = 'wasmtime' diff --git a/getrandom/.gitignore b/getrandom/.gitignore new file mode 100644 index 0000000..435eeee --- /dev/null +++ b/getrandom/.gitignore @@ -0,0 +1,6 @@ +/target +**/*.rs.bk +Cargo.lock +*.ts +*.js +*.wasm diff --git a/getrandom/.travis.yml b/getrandom/.travis.yml new file mode 100644 index 0000000..bdd9e72 --- /dev/null +++ b/getrandom/.travis.yml @@ -0,0 +1,221 @@ +language: rust +sudo: false + +matrix: + include: + - name: "Linux, 1.32.0" + rust: 1.32.0 + os: linux + + - name: "OSX, 1.32.0" + rust: 1.32.0 + os: osx + + - name: "Linux, stable" + rust: stable + + - name: "OSX+iOS, stable" + rust: stable + os: osx + install: + - rustup target add aarch64-apple-ios + script: + - cargo test + - cargo test --examples + - cargo build --target aarch64-apple-ios + + - name: "Linux, beta" + rust: beta + + - name: "WASM via emscripten, stdweb, wasm-bindgen and WASI" + rust: nightly + addons: + firefox: latest + chrome: stable + install: + - rustup target add wasm32-unknown-unknown + - rustup target add wasm32-unknown-emscripten + - rustup target add asmjs-unknown-emscripten + - rustup target add wasm32-wasi + # Get latest geckodriver + - export VERSION=$(curl -s https://api.github.com/repos/mozilla/geckodriver/releases/latest | jq -r ".tag_name") + - wget -O geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/$VERSION/geckodriver-$VERSION-linux64.tar.gz + - tar -xzf geckodriver.tar.gz + # Get latest chromedirver + - export VERSION=$(wget -q -O - https://chromedriver.storage.googleapis.com/LATEST_RELEASE) + - wget -O chromedriver.zip https://chromedriver.storage.googleapis.com/$VERSION/chromedriver_linux64.zip + - unzip chromedriver.zip + # Get cargo-web + - export VERSION=0.6.26 # Pin version for stability + - wget -O cargo-web.gz https://github.com/koute/cargo-web/releases/download/$VERSION/cargo-web-x86_64-unknown-linux-gnu.gz + - gunzip cargo-web.gz + - chmod +x cargo-web + # Get wasmtime + - export VERSION=v0.3.0 # Pin version for stability + - wget -O wasmtime.tar.xz https://github.com/CraneStation/wasmtime/releases/download/$VERSION/wasmtime-$VERSION-x86_64-linux.tar.xz + - tar -xf wasmtime.tar.xz --strip-components=1 + # Get wasm-bindgen-test-runner which matches our wasm-bindgen version + - export VERSION=$(cargo metadata --format-version=1 | jq -r '.packages[] | select ( .name == "wasm-bindgen" ) | .version') + - wget -O wasm-bindgen.tar.gz https://github.com/rustwasm/wasm-bindgen/releases/download/$VERSION/wasm-bindgen-$VERSION-x86_64-unknown-linux-musl.tar.gz + - tar -xzf wasm-bindgen.tar.gz --strip-components=1 + # Place the runner binaries in our PATH + - mv cargo-web wasmtime wasm-bindgen-test-runner $HOME/.cargo/bin + # Download and setup emscripten + - cargo web prepare-emscripten + env: EMCC_CFLAGS="-s ERROR_ON_UNDEFINED_SYMBOLS=0" + script: + # We cannot run emscripten test binaries (see rust-lang/rust#63649). + # However, we can still build and link all tests to make sure that works. + # This is actually useful as it finds stuff such as rust-random/rand#669 + - cargo web test --target wasm32-unknown-emscripten --no-run + - cargo web test --target asmjs-unknown-emscripten --no-run + # wasi tests + - cargo test --target wasm32-wasi + # stdweb tests (Node, Chrome) + - cargo web test --nodejs --target=wasm32-unknown-unknown --features=stdweb + - cargo web test --target=wasm32-unknown-unknown --features=stdweb + # wasm-bindgen tests (Node, Firefox, Chrome) + - cargo test --target wasm32-unknown-unknown --features=wasm-bindgen + - GECKODRIVER=$PWD/geckodriver cargo test --target wasm32-unknown-unknown --features=test-in-browser + - CHROMEDRIVER=$PWD/chromedriver cargo test --target wasm32-unknown-unknown --features=test-in-browser + + - name: "Linux, nightly, docs" + rust: nightly + os: linux + install: + - cargo --list | egrep "^\s*deadlinks$" -q || cargo install cargo-deadlinks + - cargo deadlinks -V + script: + - cargo test + - cargo test --benches + - cargo test --examples + # remove cached documentation, otherwise files from previous PRs can get included + - rm -rf target/doc + - cargo doc --no-deps --all --features=std,log + - cargo deadlinks --dir target/doc + # also test minimum dependency versions are usable + - cargo generate-lockfile -Z minimal-versions + - cargo test + + - name: "OSX, nightly, docs" + rust: nightly + os: osx + install: + - cargo --list | egrep "^\s*deadlinks$" -q || cargo install cargo-deadlinks + - cargo deadlinks -V + script: + - cargo test + - cargo test --benches + - cargo test --examples + # remove cached documentation, otherwise files from previous PRs can get included + - rm -rf target/doc + - cargo doc --no-deps --all --features=std,log + - cargo deadlinks --dir target/doc + # also test minimum dependency versions are usable + - cargo generate-lockfile -Z minimal-versions + - cargo test + + - name: "cross-platform build only" + rust: nightly + install: + - rustup target add x86_64-sun-solaris + - rustup target add x86_64-unknown-cloudabi + - rustup target add x86_64-unknown-freebsd + - rustup target add x86_64-fuchsia + - rustup target add x86_64-unknown-netbsd + - rustup target add x86_64-unknown-redox + - rustup target add x86_64-fortanix-unknown-sgx + # For no_std targets + - rustup component add rust-src + - cargo install cargo-xbuild || true + script: + - cargo build --target=x86_64-sun-solaris + - cargo build --target=x86_64-unknown-cloudabi + - cargo build --target=x86_64-unknown-freebsd + - cargo build --target=x86_64-fuchsia + - cargo build --target=x86_64-unknown-netbsd + - cargo build --target=x86_64-unknown-redox + - cargo build --target=x86_64-fortanix-unknown-sgx + - cargo xbuild --target=x86_64-unknown-uefi + - cargo xbuild --target=x86_64-unknown-hermit + - cargo xbuild --target=x86_64-unknown-l4re-uclibc + - cargo xbuild --target=x86_64-wrs-vxworks + # also test minimum dependency versions are usable + - cargo generate-lockfile -Z minimal-versions + - cargo build --target=x86_64-sun-solaris + - cargo build --target=x86_64-unknown-cloudabi + - cargo build --target=x86_64-unknown-freebsd + - cargo build --target=x86_64-fuchsia + - cargo build --target=x86_64-unknown-netbsd + - cargo build --target=x86_64-unknown-redox + - cargo build --target=x86_64-fortanix-unknown-sgx + - cargo xbuild --target=x86_64-unknown-uefi + - cargo xbuild --target=x86_64-unknown-hermit + - cargo xbuild --target=x86_64-unknown-l4re-uclibc + - cargo xbuild --target=x86_64-uwp-windows-gnu + + # Trust cross-built/emulated targets. We must repeat all non-default values. + - name: "Linux (MIPS, big-endian)" + env: TARGET=mips-unknown-linux-gnu + rust: stable + sudo: required + dist: trusty + services: docker + install: + - sh utils/ci/install.sh + - source ~/.cargo/env || true + script: + - bash utils/ci/script.sh + + - name: "Android (ARMv7)" + env: TARGET=armv7-linux-androideabi + rust: stable + sudo: required + dist: trusty + services: docker + install: + - sh utils/ci/install.sh + - source ~/.cargo/env || true + script: + - bash utils/ci/script.sh + + - name: "rustfmt" + rust: stable + install: + - rustup component add rustfmt + script: + - cargo fmt --all -- */*.rs --check + + allow_failures: + # Formatting errors should appear in Travis, but not break the build. + - name: "rustfmt" + +before_install: + - set -e + - rustup self update + +before_script: + - export RUSTFLAGS="-D warnings" + +script: + - cargo test + - cargo test --examples + +after_script: set +e + +cache: + cargo: true + directories: + - .local/share/cargo-web + +before_cache: + # Travis can't cache files that are not readable by "others" + - chmod -R a+r $HOME/.cargo + +notifications: + email: + on_success: never + +branches: + only: + - master diff --git a/getrandom/CHANGELOG.md b/getrandom/CHANGELOG.md new file mode 100644 index 0000000..2f6e9f4 --- /dev/null +++ b/getrandom/CHANGELOG.md @@ -0,0 +1,158 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.1.13] - 2019-08-25 +### Added +- VxWorks targets support. [#86] + +### Changed +- If zero-length slice is passed to the `getrandom` function, always return +`Ok(())` immediately without doing any calls to the underlying operating +system. [#104] +- Use the `kern.arandom` sysctl on NetBSD. [#115] + +### Fixed +- Bump `cfg-if` minimum version from 0.1.0 to 0.1.2. [#112] +- Typos and bad doc links. [#117] + +[#86]: https://github.com/rust-random/getrandom/pull/86 +[#104]: https://github.com/rust-random/getrandom/pull/104 +[#112]: https://github.com/rust-random/getrandom/pull/112 +[#115]: https://github.com/rust-random/getrandom/pull/115 +[#117]: https://github.com/rust-random/getrandom/pull/117 + +## [0.1.12] - 2019-08-18 +### Changed +- Update wasi dependency from v0.5 to v0.7. [#100] + +[#100]: https://github.com/rust-random/getrandom/pull/100 + +## [0.1.11] - 2019-08-25 +### Fixed +- Implement `std`-dependent traits for selected targets even if `std` +feature is disabled. (backward compatibility with v0.1.8) [#96] + +[#96]: https://github.com/rust-random/getrandom/pull/96 + +## [0.1.10] - 2019-08-18 [YANKED] +### Changed +- Use the dummy implementation on `wasm32-unknown-unknown` even with the +disabled `dummy` feature. [#90] + +### Fixed +- Fix CSP error for `wasm-bindgen`. [#92] + +[#90]: https://github.com/rust-random/getrandom/pull/90 +[#92]: https://github.com/rust-random/getrandom/pull/92 + +## [0.1.9] - 2019-08-14 [YANKED] +### Changed +- Remove `std` dependency for opening and reading files. [#58] +- Use `wasi` isntead of `libc` on WASI target. [#64] +- By default emit a compile-time error when built for an unsupported target. +This behaviour can be disabled by using the `dummy` feature. [#71] + +### Added +- Add support for UWP targets. [#69] +- Add unstable `rustc-dep-of-std` feature. [#78] + +[#58]: https://github.com/rust-random/getrandom/pull/58 +[#64]: https://github.com/rust-random/getrandom/pull/64 +[#69]: https://github.com/rust-random/getrandom/pull/69 +[#71]: https://github.com/rust-random/getrandom/pull/71 +[#78]: https://github.com/rust-random/getrandom/pull/78 + +## [0.1.8] - 2019-07-29 +### Changed +- Explicitly specify types to arguments of 'libc::syscall'. [#74] + +[#74]: https://github.com/rust-random/getrandom/pull/74 + +## [0.1.7] - 2019-07-29 +### Added +- Support for hermit and l4re. [#61] +- `Error::raw_os_error` method, `Error::INTERNAL_START` and +`Error::CUSTOM_START` constants. Use `libc` for retrieving OS error descriptions. [#54] + +### Changed +- Remove `lazy_static` dependency and use custom structures for lock-free +initialization. [#51] [#52] +- Try `getrandom()` first on FreeBSD. [#57] + +### Removed +- Bitrig support. [#56] + +### Deprecated +- `Error::UNKNOWN`, `Error::UNAVAILABLE`. [#54] + +[#51]: https://github.com/rust-random/getrandom/pull/51 +[#52]: https://github.com/rust-random/getrandom/pull/52 +[#54]: https://github.com/rust-random/getrandom/pull/54 +[#56]: https://github.com/rust-random/getrandom/pull/56 +[#57]: https://github.com/rust-random/getrandom/pull/57 +[#61]: https://github.com/rust-random/getrandom/pull/61 + +## [0.1.6] - 2019-06-30 +### Changed +- Minor change of RDRAND AMD bug handling. [#48] + +[#48]: https://github.com/rust-random/getrandom/pull/48 + +## [0.1.5] - 2019-06-29 +### Fixed +- Use shared `File` instead of shared file descriptor. [#44] +- Workaround for RDRAND hardware bug present on some AMD CPUs. [#43] + +### Changed +- Try `getentropy` and then fallback to `/dev/random` on macOS. [#38] + +[#38]: https://github.com/rust-random/getrandom/issues/38 +[#43]: https://github.com/rust-random/getrandom/pull/43 +[#44]: https://github.com/rust-random/getrandom/issues/44 + +## [0.1.4] - 2019-06-28 +### Added +- Add support for `x86_64-unknown-uefi` target by using RDRAND with CPUID +feature detection. [#30] + +### Fixed +- Fix long buffer issues on Windows and Linux. [#31] [#32] +- Check `EPERM` in addition to `ENOSYS` on Linux. [#37] + +### Changed +- Improve efficiency by sharing file descriptor across threads. [#13] +- Remove `cloudabi`, `winapi`, and `fuchsia-cprng` dependencies. [#40] +- Improve RDRAND implementation. [#24] +- Don't block during syscall detection on Linux. [#26] +- Increase consistency with libc implementation on FreeBSD. [#36] +- Apply `rustfmt`. [#39] + +[#30]: https://github.com/rust-random/getrandom/pull/30 +[#13]: https://github.com/rust-random/getrandom/issues/13 +[#40]: https://github.com/rust-random/getrandom/pull/40 +[#26]: https://github.com/rust-random/getrandom/pull/26 +[#24]: https://github.com/rust-random/getrandom/pull/24 +[#39]: https://github.com/rust-random/getrandom/pull/39 +[#36]: https://github.com/rust-random/getrandom/pull/36 +[#31]: https://github.com/rust-random/getrandom/issues/31 +[#32]: https://github.com/rust-random/getrandom/issues/32 +[#37]: https://github.com/rust-random/getrandom/issues/37 + +## [0.1.3] - 2019-05-15 +- Update for `wasm32-unknown-wasi` being renamed to `wasm32-wasi`, and for + WASI being categorized as an OS. + +## [0.1.2] - 2019-04-06 +- Add support for `wasm32-unknown-wasi` target. + +## [0.1.1] - 2019-04-05 +- Enable std functionality for CloudABI by default. + +## [0.1.0] - 2019-03-23 +Publish initial implementation. + +## [0.0.0] - 2019-01-19 +Publish an empty template library. diff --git a/getrandom/Cargo.toml b/getrandom/Cargo.toml new file mode 100644 index 0000000..b159674 --- /dev/null +++ b/getrandom/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "getrandom" +version = "0.1.13" +edition = "2018" +authors = ["The Rand Project Developers"] +license = "MIT OR Apache-2.0" +description = "A small cross-platform library for retrieving random data from system source" +documentation = "https://docs.rs/getrandom" +repository = "https://github.com/rust-random/getrandom" +categories = ["os", "no-std"] +exclude = ["utils/*", ".*", "appveyor.yml"] + +[badges] +travis-ci = { repository = "rust-random/getrandom" } +appveyor = { repository = "rust-random/getrandom" } + +[dependencies] +log = { version = "0.4", optional = true } +cfg-if = "0.1.2" + +# When built as part of libstd +compiler_builtins = { version = "0.1", optional = true } +core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" } + +[target.'cfg(unix)'.dependencies] +libc = { version = "0.2.64", default-features = false } + +[target.'cfg(target_os = "wasi")'.dependencies] +wasi = "0.7" + +[target.wasm32-unknown-unknown.dependencies] +wasm-bindgen = { version = "0.2.29", optional = true } +stdweb = { version = "0.4.18", optional = true } + +[target.wasm32-unknown-unknown.dev-dependencies] +wasm-bindgen-test = "0.2" + +[features] +std = [] +# Enables dummy implementation for unsupported targets +dummy = [] +# Unstable feature to support being a libstd dependency +rustc-dep-of-std = ["compiler_builtins", "core"] +# Unstable feature for testing +test-in-browser = ["wasm-bindgen"] diff --git a/getrandom/LICENSE-APACHE b/getrandom/LICENSE-APACHE new file mode 100644 index 0000000..17d7468 --- /dev/null +++ b/getrandom/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/getrandom/LICENSE-MIT b/getrandom/LICENSE-MIT new file mode 100644 index 0000000..d93b5ba --- /dev/null +++ b/getrandom/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/getrandom/README.md b/getrandom/README.md new file mode 100644 index 0000000..01bbfb5 --- /dev/null +++ b/getrandom/README.md @@ -0,0 +1,78 @@ +# getrandom + +[![Build Status](https://travis-ci.org/rust-random/getrandom.svg?branch=master)](https://travis-ci.org/rust-random/getrandom) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-random/getrandom?svg=true)](https://ci.appveyor.com/project/rust-random/getrandom) +[![Crate](https://img.shields.io/crates/v/getrandom.svg)](https://crates.io/crates/getrandom) +[![Documentation](https://docs.rs/getrandom/badge.svg)](https://docs.rs/getrandom) +[![Dependency status](https://deps.rs/repo/github/rust-random/getrandom/status.svg)](https://deps.rs/repo/github/rust-random/getrandom) + + +A Rust library for retrieving random data from (operating) system source. It is +assumed that system always provides high-quality cryptographically secure random +data, ideally backed by hardware entropy sources. This crate derives its name +from Linux's `getrandom` function, but is cross platform, roughly supporting +the same set of platforms as Rust's `std` lib. + +This is a low-level API. Most users should prefer using high-level random-number +library like [`rand`]. + +[`rand`]: https://crates.io/crates/rand + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +getrandom = "0.1" +``` + +Then invoke the `getrandom` function: + +```rust +fn get_random_buf() -> Result<[u8; 32], getrandom::Error> { + let mut buf = [0u8; 32]; + getrandom::getrandom(&mut buf)?; + Ok(buf) +} +``` + +## Features + +This library is `no_std` for every supported target. However, getting randomness +usually requires calling some external system API. This means most platforms +will require linking against system libraries (i.e. `libc` for Unix, +`Advapi32.dll` for Windows, Security framework on iOS, etc...). + +The `log` library is supported as an optional dependency. If enabled, error +reporting will be improved on some platforms. + +For the `wasm32-unknown-unknown` target, one of the following features should be +enabled: + +- [`wasm-bindgen`](https://crates.io/crates/wasm_bindgen) +- [`stdweb`](https://crates.io/crates/stdweb) + +By default, compiling `getrandom` for an unsupported target will result in +a compilation error. If you want to build an application which uses `getrandom` +for such target, you can either: +- Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml` +to switch to a custom implementation with a support of your target. +- Enable the `dummy` feature to have getrandom use an implementation that always +fails at run-time on unsupported targets. + +[replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section +[patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section + +## Minimum Supported Rust Version + +This crate requires Rust 1.32.0 or later. + +# License + +The `getrandom` library is distributed under either of + + * [Apache License, Version 2.0](LICENSE-APACHE) + * [MIT license](LICENSE-MIT) + +at your option. diff --git a/getrandom/appveyor.yml b/getrandom/appveyor.yml new file mode 100644 index 0000000..7a34e32 --- /dev/null +++ b/getrandom/appveyor.yml @@ -0,0 +1,52 @@ +environment: + + # At the time this was added AppVeyor was having troubles with checking + # revocation of SSL certificates of sites like static.rust-lang.org and what + # we think is crates.io. The libcurl HTTP client by default checks for + # revocation on Windows and according to a mailing list [1] this can be + # disabled. + # + # The `CARGO_HTTP_CHECK_REVOKE` env var here tells cargo to disable SSL + # revocation checking on Windows in libcurl. Note, though, that rustup, which + # we're using to download Rust here, also uses libcurl as the default backend. + # Unlike Cargo, however, rustup doesn't have a mechanism to disable revocation + # checking. To get rustup working we set `RUSTUP_USE_HYPER` which forces it to + # use the Hyper instead of libcurl backend. Both Hyper and libcurl use + # schannel on Windows but it appears that Hyper configures it slightly + # differently such that revocation checking isn't turned on by default. + # + # [1]: https://curl.haxx.se/mail/lib-2016-03/0202.html + RUSTUP_USE_HYPER: 1 + CARGO_HTTP_CHECK_REVOKE: false + + matrix: + - TARGET: x86_64-pc-windows-msvc + CHANNEL: 1.32.0 + - TARGET: x86_64-pc-windows-msvc + CHANNEL: stable + - TARGET: x86_64-pc-windows-msvc + CHANNEL: nightly + - TARGET: i686-pc-windows-msvc + CHANNEL: nightly + - TARGET: x86_64-pc-windows-gnu + CHANNEL: nightly + - TARGET: i686-pc-windows-gnu + CHANNEL: nightly + +install: + - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init.exe -y --default-host %TARGET% --default-toolchain %CHANNEL% + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - set RUSTFLAGS=-D warnings + - cargo test + - cargo test --examples + +branches: + only: + - master diff --git a/getrandom/benches/mod.rs b/getrandom/benches/mod.rs new file mode 100644 index 0000000..07953f1 --- /dev/null +++ b/getrandom/benches/mod.rs @@ -0,0 +1,23 @@ +#![feature(test)] +extern crate getrandom; +extern crate test; + +#[bench] +fn bench_64(b: &mut test::Bencher) { + let mut buf = [0u8; 64]; + b.iter(|| { + getrandom::getrandom(&mut buf[..]).unwrap(); + test::black_box(&buf); + }); + b.bytes = buf.len() as u64; +} + +#[bench] +fn bench_65536(b: &mut test::Bencher) { + let mut buf = [0u8; 65536]; + b.iter(|| { + getrandom::getrandom(&mut buf[..]).unwrap(); + test::black_box(&buf); + }); + b.bytes = buf.len() as u64; +} diff --git a/getrandom/build.rs b/getrandom/build.rs new file mode 100644 index 0000000..1beb4ed --- /dev/null +++ b/getrandom/build.rs @@ -0,0 +1,19 @@ +#![deny(warnings)] + +use std::env; + +fn main() { + let target = env::var("TARGET").expect("TARGET was not set"); + if target.contains("-uwp-windows-") { + // for BCryptGenRandom + println!("cargo:rustc-link-lib=bcrypt"); + // to work around unavailability of `target_vendor` on Rust 1.33 + println!("cargo:rustc-cfg=getrandom_uwp"); + } else if target.contains("windows") { + // for RtlGenRandom (aka SystemFunction036) + println!("cargo:rustc-link-lib=advapi32"); + } else if target.contains("apple-ios") { + // for SecRandomCopyBytes and kSecRandomDefault + println!("cargo:rustc-link-lib=framework=Security"); + } +} diff --git a/getrandom/src/bsd_arandom.rs b/getrandom/src/bsd_arandom.rs new file mode 100644 index 0000000..eb564ff --- /dev/null +++ b/getrandom/src/bsd_arandom.rs @@ -0,0 +1,49 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for FreeBSD and NetBSD +use crate::util_libc::sys_fill_exact; +use crate::Error; +use core::ptr; + +fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { + static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND]; + let mut len = buf.len(); + let ret = unsafe { + libc::sysctl( + MIB.as_ptr(), + MIB.len() as libc::c_uint, + buf.as_mut_ptr() as *mut _, + &mut len, + ptr::null(), + 0, + ) + }; + if ret == -1 { + error!("sysctl kern.arandom: syscall failed"); + -1 + } else { + len as libc::ssize_t + } +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + #[cfg(target_os = "freebsd")] + { + use crate::util_libc::Weak; + static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; + type GetRandomFn = + unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; + + if let Some(fptr) = GETRANDOM.ptr() { + let func: GetRandomFn = unsafe { core::mem::transmute(fptr) }; + return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) }); + } + } + sys_fill_exact(dest, kern_arnd) +} diff --git a/getrandom/src/cloudabi.rs b/getrandom/src/cloudabi.rs new file mode 100644 index 0000000..d3d0928 --- /dev/null +++ b/getrandom/src/cloudabi.rs @@ -0,0 +1,25 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for CloudABI +use crate::Error; +use core::num::NonZeroU32; + +extern "C" { + fn cloudabi_sys_random_get(buf: *mut u8, buf_len: usize) -> u16; +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + let errno = unsafe { cloudabi_sys_random_get(dest.as_mut_ptr(), dest.len()) }; + if let Some(code) = NonZeroU32::new(errno as u32) { + error!("cloudabi_sys_random_get: failed with {}", errno); + Err(Error::from(code)) + } else { + Ok(()) // Zero means success for CloudABI + } +} diff --git a/getrandom/src/dummy.rs b/getrandom/src/dummy.rs new file mode 100644 index 0000000..0c24ba0 --- /dev/null +++ b/getrandom/src/dummy.rs @@ -0,0 +1,14 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A dummy implementation for unsupported targets which always fails +use crate::{error::UNSUPPORTED, Error}; + +pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> { + Err(UNSUPPORTED) +} diff --git a/getrandom/src/error.rs b/getrandom/src/error.rs new file mode 100644 index 0000000..b2cb9a8 --- /dev/null +++ b/getrandom/src/error.rs @@ -0,0 +1,178 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use core::fmt; +use core::num::NonZeroU32; + +/// A small and `no_std` compatible error type. +/// +/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and +/// if so, which error code the OS gave the application. If such an error is +/// encountered, please consult with your system documentation. +/// +/// Internally this type is a NonZeroU32, with certain values reserved for +/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Error(NonZeroU32); + +impl Error { + #[deprecated(since = "0.1.7")] + /// Unknown error. + pub const UNKNOWN: Error = UNSUPPORTED; + #[deprecated(since = "0.1.7")] + /// System entropy source is unavailable. + pub const UNAVAILABLE: Error = UNSUPPORTED; + + /// Codes below this point represent OS Errors (i.e. positive i32 values). + /// Codes at or above this point, but below [`Error::CUSTOM_START`] are + /// reserved for use by the `rand` and `getrandom` crates. + pub const INTERNAL_START: u32 = 1 << 31; + + /// Codes at or above this point can be used by users to define their own + /// custom errors. + pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); + + /// Extract the raw OS error code (if this error came from the OS) + /// + /// This method is identical to `std::io::Error::raw_os_error()`, except + /// that it works in `no_std` contexts. If this method returns `None`, the + /// error value can still be formatted via the `Display` implementation. + #[inline] + pub fn raw_os_error(self) -> Option<i32> { + if self.0.get() < Self::INTERNAL_START { + Some(self.0.get() as i32) + } else { + None + } + } + + /// Extract the bare error code. + /// + /// This code can either come from the underlying OS, or be a custom error. + /// Use [`Error::raw_os_error()`] to disambiguate. + #[inline] + pub fn code(self) -> NonZeroU32 { + self.0 + } +} + +cfg_if! { + if #[cfg(unix)] { + fn os_err_desc(errno: i32, buf: &mut [u8]) -> Option<&str> { + let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; + if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { + return None; + } + + // Take up to trailing null byte + let n = buf.len(); + let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); + core::str::from_utf8(&buf[..idx]).ok() + } + } else if #[cfg(target_os = "wasi")] { + fn os_err_desc(errno: i32, _buf: &mut [u8]) -> Option<&str> { + core::num::NonZeroU16::new(errno as u16) + .and_then(wasi::wasi_unstable::error_str) + } + } else { + fn os_err_desc(_errno: i32, _buf: &mut [u8]) -> Option<&str> { + None + } + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut dbg = f.debug_struct("Error"); + if let Some(errno) = self.raw_os_error() { + dbg.field("os_error", &errno); + let mut buf = [0u8; 128]; + if let Some(desc) = os_err_desc(errno, &mut buf) { + dbg.field("description", &desc); + } + } else if let Some(desc) = internal_desc(*self) { + dbg.field("internal_code", &self.0.get()); + dbg.field("description", &desc); + } else { + dbg.field("unknown_code", &self.0.get()); + } + dbg.finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(errno) = self.raw_os_error() { + let mut buf = [0u8; 128]; + match os_err_desc(errno, &mut buf) { + Some(desc) => f.write_str(desc), + None => write!(f, "OS Error: {}", errno), + } + } else if let Some(desc) = internal_desc(*self) { + f.write_str(desc) + } else { + write!(f, "Unknown Error: {}", self.0.get()) + } + } +} + +impl From<NonZeroU32> for Error { + fn from(code: NonZeroU32) -> Self { + Self(code) + } +} + +// TODO: Convert to a function when min_version >= 1.33 +macro_rules! internal_error { + ($n:expr) => { + Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) }) + }; +} + +/// Internal Error constants +pub(crate) const UNSUPPORTED: Error = internal_error!(0); +pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1); +pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2); +pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3); +pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4); +pub(crate) const FAILED_RDRAND: Error = internal_error!(5); +pub(crate) const NO_RDRAND: Error = internal_error!(6); +pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7); +pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8); +pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9); +pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10); +pub(crate) const RAND_SECURE_FATAL: Error = internal_error!(11); + +fn internal_desc(error: Error) -> Option<&'static str> { + match error { + UNSUPPORTED => Some("getrandom: this target is not supported"), + ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), + UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"), + SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"), + RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"), + FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), + NO_RDRAND => Some("RDRAND: instruction not supported"), + BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"), + BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"), + STDWEB_NO_RNG => Some("stdweb: no randomness source available"), + STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"), + RAND_SECURE_FATAL => Some("randSecure: random number generator module is not initialized"), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::Error; + use core::mem::size_of; + + #[test] + fn test_size() { + assert_eq!(size_of::<Error>(), 4); + assert_eq!(size_of::<Result<(), Error>>(), 4); + } +} diff --git a/getrandom/src/error_impls.rs b/getrandom/src/error_impls.rs new file mode 100644 index 0000000..007472e --- /dev/null +++ b/getrandom/src/error_impls.rs @@ -0,0 +1,35 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +extern crate std; + +use crate::{error::UNKNOWN_IO_ERROR, Error}; +use core::convert::From; +use core::num::NonZeroU32; +use std::io; + +impl From<io::Error> for Error { + fn from(err: io::Error) -> Self { + if let Some(errno) = err.raw_os_error() { + if let Some(code) = NonZeroU32::new(errno as u32) { + return Error::from(code); + } + } + UNKNOWN_IO_ERROR + } +} + +impl From<Error> for io::Error { + fn from(err: Error) -> Self { + match err.raw_os_error() { + Some(errno) => io::Error::from_raw_os_error(errno), + None => io::Error::new(io::ErrorKind::Other, err), + } + } +} + +impl std::error::Error for Error {} diff --git a/getrandom/src/fuchsia.rs b/getrandom/src/fuchsia.rs new file mode 100644 index 0000000..572ff53 --- /dev/null +++ b/getrandom/src/fuchsia.rs @@ -0,0 +1,20 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Fuchsia Zircon +use crate::Error; + +#[link(name = "zircon")] +extern "C" { + fn zx_cprng_draw(buffer: *mut u8, length: usize); +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) } + Ok(()) +} diff --git a/getrandom/src/ios.rs b/getrandom/src/ios.rs new file mode 100644 index 0000000..30c008c --- /dev/null +++ b/getrandom/src/ios.rs @@ -0,0 +1,31 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for iOS +use crate::{error::SEC_RANDOM_FAILED, Error}; + +// TODO: Make extern once extern_types feature is stabilized. See: +// https://github.com/rust-lang/rust/issues/43467 +#[repr(C)] +struct SecRandom([u8; 0]); + +#[link(name = "Security", kind = "framework")] +extern "C" { + static kSecRandomDefault: *const SecRandom; + + fn SecRandomCopyBytes(rnd: *const SecRandom, count: usize, bytes: *mut u8) -> i32; +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) }; + if ret == -1 { + Err(SEC_RANDOM_FAILED) + } else { + Ok(()) + } +} diff --git a/getrandom/src/lib.rs b/getrandom/src/lib.rs new file mode 100644 index 0000000..c305406 --- /dev/null +++ b/getrandom/src/lib.rs @@ -0,0 +1,282 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Interface to the random number generator of the operating system. +//! +//! # Platform sources +//! +//! | OS | interface +//! |------------------|--------------------------------------------------------- +//! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` +//! | Windows | [`RtlGenRandom`][3] +//! | macOS | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`) +//! | iOS | [`SecRandomCopyBytes`][4] +//! | FreeBSD | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5] +//! | OpenBSD | [`getentropy`][6] +//! | NetBSD | [`kern.arandom`][7] +//! | Dragonfly BSD | [`/dev/random`][8] +//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10] +//! | Fuchsia OS | [`cprng_draw`][11] +//! | Redox | [`rand:`][12] +//! | CloudABI | [`cloudabi_sys_random_get`][13] +//! | Haiku | `/dev/random` (identical to `/dev/urandom`) +//! | L4RE, SGX, UEFI | [RDRAND][18] +//! | Hermit | [RDRAND][18] as [`sys_rand`][22] is currently broken. +//! | VxWorks | `randABytes` after checking entropy pool initialization with `randSecure` +//! | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and asm.js][16]) +//! | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and asm.js][16]) +//! | WASI | [`__wasi_random_get`][17] +//! +//! Getrandom doesn't have a blanket implementation for all Unix-like operating +//! systems that reads from `/dev/urandom`. This ensures all supported operating +//! systems are using the recommended interface and respect maximum buffer +//! sizes. +//! +//! ## Unsupported targets +//! +//! By default, compiling `getrandom` for an unsupported target will result in +//! a compilation error. If you want to build an application which uses `getrandom` +//! for such target, you can either: +//! - Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml` +//! to switch to a custom implementation with a support of your target. +//! - Enable the `dummy` feature to have getrandom use an implementation that always +//! fails at run-time on unsupported targets. +//! +//! [replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section +//! [patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section +//! +//! ## Support for WebAssembly and asm.js +//! +//! Getrandom supports all of Rust's current `wasm32` targets, and it works with +//! both Node.js and web browsers. The three Emscripten targets +//! `asmjs-unknown-emscripten`, `wasm32-unknown-emscripten`, and +//! `wasm32-experimental-emscripten` use Emscripten's `/dev/random` emulation. +//! The WASI target `wasm32-wasi` uses the [`__wasi_random_get`][17] function +//! defined by the WASI standard. +//! +//! Getrandom also supports `wasm32-unknown-unknown` by directly calling +//! JavaScript methods. Rust currently has two ways to do this: [bindgen] and +//! [stdweb]. Getrandom supports using either one by enabling the +//! `wasm-bindgen` or `stdweb` crate features. Note that if both features are +//! enabled, `wasm-bindgen` will be used. If neither feature is enabled, calls +//! to `getrandom` will always fail at runtime. +//! +//! [bindgen]: https://github.com/rust-lang/rust-bindgen +//! [stdweb]: https://github.com/koute/stdweb +//! +//! ## Early boot +//! +//! It is possible that early in the boot process the OS hasn't had enough time +//! yet to collect entropy to securely seed its RNG, especially on virtual +//! machines. +//! +//! Some operating systems always block the thread until the RNG is securely +//! seeded. This can take anywhere from a few seconds to more than a minute. +//! Others make a best effort to use a seed from before the shutdown and don't +//! document much. +//! +//! A few, Linux, NetBSD and Solaris, offer a choice between blocking and +//! getting an error; in these cases we always choose to block. +//! +//! On Linux (when the `getrandom` system call is not available) and on NetBSD +//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected +//! enough entropy yet. To avoid returning low-entropy bytes, we first read from +//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded. +//! +//! # Error handling +//! +//! We always choose failure over returning insecure "random" bytes. In general, +//! on supported platforms, failure is highly unlikely, though not impossible. +//! If an error does occur, then it is likely that it will occur on every call to +//! `getrandom`, hence after the first successful call one can be reasonably +//! confident that no errors will occur. +//! +//! On unsupported platforms, `getrandom` always fails. See the [`Error`] type +//! for more information on what data is returned on failure. +//! +//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html +//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html +//! [3]: https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom +//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc +//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 +//! [6]: https://man.openbsd.org/getentropy.2 +//! [7]: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7+NetBSD-8.0 +//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random§ion=4 +//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html +//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html +//! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw +//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs +//! [13]: https://github.com/nuxinl/cloudabi#random_get +//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues +//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback +//! [16]: #support-for-webassembly-and-asmjs +//! [17]: https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md#__wasi_random_get +//! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide +//! [19]: https://www.unix.com/man-page/mojave/2/getentropy/ +//! [20]: https://www.unix.com/man-page/mojave/4/random/ +//! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable +//! [22]: https://github.com/hermitcore/libhermit-rs/blob/09c38b0371cee6f56a541400ba453e319e43db53/src/syscalls/random.rs#L21 + +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://rust-random.github.io/rand/" +)] +#![no_std] +#![cfg_attr(feature = "stdweb", recursion_limit = "128")] +#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] + +#[macro_use] +extern crate cfg_if; + +cfg_if! { + if #[cfg(feature = "log")] { + #[allow(unused)] + #[macro_use] + extern crate log; + } else { + #[allow(unused)] + macro_rules! error { + ($($x:tt)*) => {}; + } + #[allow(unused)] + macro_rules! warn { + ($($x:tt)*) => {}; + } + #[allow(unused)] + macro_rules! info { + ($($x:tt)*) => {}; + } + } +} + +mod error; +pub use crate::error::Error; + +#[allow(dead_code)] +mod util; + +#[cfg(target_os = "vxworks")] +#[allow(dead_code)] +mod util_libc; + +cfg_if! { + // Unlike the other Unix, Fuchsia and iOS don't use the libc to make any calls. + if #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "emscripten", + target_os = "freebsd", target_os = "haiku", target_os = "illumos", + target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "openbsd", target_os = "redox", target_os = "solaris"))] { + #[allow(dead_code)] + mod util_libc; + // Keep std-only trait definitions for backwards compatibility + mod error_impls; + } else if #[cfg(feature = "std")] { + mod error_impls; + } +} + +// These targets read from a file as a fallback method. +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "illumos", +))] +mod use_file; + +// System-specific implementations. +// +// These should all provide getrandom_inner with the same signature as getrandom. +cfg_if! { + if #[cfg(target_os = "android")] { + #[path = "linux_android.rs"] mod imp; + } else if #[cfg(target_os = "cloudabi")] { + #[path = "cloudabi.rs"] mod imp; + } else if #[cfg(target_os = "dragonfly")] { + #[path = "use_file.rs"] mod imp; + } else if #[cfg(target_os = "emscripten")] { + #[path = "use_file.rs"] mod imp; + } else if #[cfg(target_os = "freebsd")] { + #[path = "bsd_arandom.rs"] mod imp; + } else if #[cfg(target_os = "fuchsia")] { + #[path = "fuchsia.rs"] mod imp; + } else if #[cfg(target_os = "haiku")] { + #[path = "use_file.rs"] mod imp; + } else if #[cfg(target_os = "illumos")] { + #[path = "solaris_illumos.rs"] mod imp; + } else if #[cfg(target_os = "ios")] { + #[path = "ios.rs"] mod imp; + } else if #[cfg(target_os = "linux")] { + #[path = "linux_android.rs"] mod imp; + } else if #[cfg(target_os = "macos")] { + #[path = "macos.rs"] mod imp; + } else if #[cfg(target_os = "netbsd")] { + #[path = "bsd_arandom.rs"] mod imp; + } else if #[cfg(target_os = "openbsd")] { + #[path = "openbsd.rs"] mod imp; + } else if #[cfg(target_os = "redox")] { + #[path = "use_file.rs"] mod imp; + } else if #[cfg(target_os = "solaris")] { + #[path = "solaris_illumos.rs"] mod imp; + } else if #[cfg(target_os = "wasi")] { + #[path = "wasi.rs"] mod imp; + } else if #[cfg(target_os = "vxworks")] { + #[path = "vxworks.rs"] mod imp; + } else if #[cfg(all(windows, getrandom_uwp))] { + #[path = "windows_uwp.rs"] mod imp; + } else if #[cfg(windows)] { + #[path = "windows.rs"] mod imp; + } else if #[cfg(all(target_arch = "x86_64", any( + target_os = "hermit", + target_os = "l4re", + target_os = "uefi", + target_env = "sgx", + )))] { + #[path = "rdrand.rs"] mod imp; + } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { + cfg_if! { + if #[cfg(feature = "wasm-bindgen")] { + #[path = "wasm32_bindgen.rs"] mod imp; + } else if #[cfg(feature = "stdweb")] { + #[path = "wasm32_stdweb.rs"] mod imp; + } else { + // Always have an implementation for wasm32-unknown-unknown. + // See https://github.com/rust-random/getrandom/issues/87 + #[path = "dummy.rs"] mod imp; + } + } + } else if #[cfg(feature = "dummy")] { + #[path = "dummy.rs"] mod imp; + } else { + compile_error!("\ + target is not supported, for more information see: \ + https://docs.rs/getrandom/#unsupported-targets\ + "); + } +} + +/// Fill `dest` with random bytes from the system's preferred random number +/// source. +/// +/// This function returns an error on any failure, including partial reads. We +/// make no guarantees regarding the contents of `dest` on error. If `dest` is +/// empty, `getrandom` immediately returns success, making no calls to the +/// underlying operating system. +/// +/// Blocking is possible, at least during early boot; see module documentation. +/// +/// In general, `getrandom` will be fast enough for interactive usage, though +/// significantly slower than a user-space CSPRNG; for the latter consider +/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html). +pub fn getrandom(dest: &mut [u8]) -> Result<(), error::Error> { + if dest.is_empty() { + return Ok(()); + } + imp::getrandom_inner(dest) +} diff --git a/getrandom/src/linux_android.rs b/getrandom/src/linux_android.rs new file mode 100644 index 0000000..a29feb5 --- /dev/null +++ b/getrandom/src/linux_android.rs @@ -0,0 +1,44 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Linux / Android +use crate::util::LazyBool; +use crate::util_libc::{last_os_error, sys_fill_exact}; +use crate::{use_file, Error}; + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + static HAS_GETRANDOM: LazyBool = LazyBool::new(); + if HAS_GETRANDOM.unsync_init(is_getrandom_available) { + sys_fill_exact(dest, |buf| unsafe { + getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0) + }) + } else { + use_file::getrandom_inner(dest) + } +} + +fn is_getrandom_available() -> bool { + let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) }; + if res < 0 { + match last_os_error().raw_os_error() { + Some(libc::ENOSYS) => false, // No kernel support + Some(libc::EPERM) => false, // Blocked by seccomp + _ => true, + } + } else { + true + } +} + +unsafe fn getrandom( + buf: *mut libc::c_void, + buflen: libc::size_t, + flags: libc::c_uint, +) -> libc::ssize_t { + libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as libc::ssize_t +} diff --git a/getrandom/src/macos.rs b/getrandom/src/macos.rs new file mode 100644 index 0000000..c3bc533 --- /dev/null +++ b/getrandom/src/macos.rs @@ -0,0 +1,34 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for macOS +use crate::util_libc::{last_os_error, Weak}; +use crate::{use_file, Error}; +use core::mem; + +type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + static GETENTROPY: Weak = unsafe { Weak::new("getentropy\0") }; + if let Some(fptr) = GETENTROPY.ptr() { + let func: GetEntropyFn = unsafe { mem::transmute(fptr) }; + for chunk in dest.chunks_mut(256) { + let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) }; + if ret != 0 { + let err = last_os_error(); + error!("getentropy syscall failed"); + return Err(err); + } + } + Ok(()) + } else { + // We fallback to reading from /dev/random instead of SecRandomCopyBytes + // to avoid high startup costs and linking the Security framework. + use_file::getrandom_inner(dest) + } +} diff --git a/getrandom/src/openbsd.rs b/getrandom/src/openbsd.rs new file mode 100644 index 0000000..e1ac179 --- /dev/null +++ b/getrandom/src/openbsd.rs @@ -0,0 +1,23 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for OpenBSD +use crate::util_libc::last_os_error; +use crate::Error; + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + for chunk in dest.chunks_mut(256) { + let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) }; + if ret == -1 { + let err = last_os_error(); + error!("libc::getentropy call failed"); + return Err(err); + } + } + Ok(()) +} diff --git a/getrandom/src/rdrand.rs b/getrandom/src/rdrand.rs new file mode 100644 index 0000000..e441682 --- /dev/null +++ b/getrandom/src/rdrand.rs @@ -0,0 +1,90 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for SGX using RDRAND instruction +use crate::error::{FAILED_RDRAND, NO_RDRAND}; +#[cfg(not(target_feature = "rdrand"))] +use crate::util::LazyBool; +use crate::Error; +use core::arch::x86_64::_rdrand64_step; +use core::mem; + +// Recommendation from "Intel® Digital Random Number Generator (DRNG) Software +// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures +// Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. +const RETRY_LIMIT: usize = 10; +const WORD_SIZE: usize = mem::size_of::<u64>(); + +#[target_feature(enable = "rdrand")] +unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { + for _ in 0..RETRY_LIMIT { + let mut el = mem::zeroed(); + if _rdrand64_step(&mut el) == 1 { + // AMD CPUs from families 14h to 16h (pre Ryzen) sometimes fail to + // set CF on bogus random data, so we check these values explicitly. + // See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505 + // We perform this check regardless of target to guard against + // any implementation that incorrectly fails to set CF. + if el != 0 && el != !0 { + return Ok(el.to_ne_bytes()); + } + error!("RDRAND returned {:X}, CPU RNG may be broken", el); + // Keep looping in case this was a false positive. + } + } + Err(FAILED_RDRAND) +} + +// "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. +#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))] +compile_error!( + "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd." +); + +#[cfg(target_feature = "rdrand")] +fn is_rdrand_supported() -> bool { + true +} + +// TODO use is_x86_feature_detected!("rdrand") when that works in core. See: +// https://github.com/rust-lang-nursery/stdsimd/issues/464 +#[cfg(not(target_feature = "rdrand"))] +fn is_rdrand_supported() -> bool { + use core::arch::x86_64::__cpuid; + // SAFETY: All x86_64 CPUs support CPUID leaf 1 + const FLAG: u32 = 1 << 30; + static HAS_RDRAND: LazyBool = LazyBool::new(); + HAS_RDRAND.unsync_init(|| unsafe { (__cpuid(1).ecx & FLAG) != 0 }) +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + if !is_rdrand_supported() { + return Err(NO_RDRAND); + } + + // SAFETY: After this point, rdrand is supported, so calling the rdrand + // functions is not undefined behavior. + unsafe { rdrand_exact(dest) } +} + +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> { + // We use chunks_exact_mut instead of chunks_mut as it allows almost all + // calls to memcpy to be elided by the compiler. + let mut chunks = dest.chunks_exact_mut(WORD_SIZE); + for chunk in chunks.by_ref() { + chunk.copy_from_slice(&rdrand()?); + } + + let tail = chunks.into_remainder(); + let n = tail.len(); + if n > 0 { + tail.copy_from_slice(&rdrand()?[..n]); + } + Ok(()) +} diff --git a/getrandom/src/solaris_illumos.rs b/getrandom/src/solaris_illumos.rs new file mode 100644 index 0000000..9473123 --- /dev/null +++ b/getrandom/src/solaris_illumos.rs @@ -0,0 +1,44 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for the Solaris family +//! +//! Read from `/dev/random`, with chunks of limited size (256 bytes). +//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A. +//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less +//! secure. We choose to read from `/dev/random`. +//! +//! Since Solaris 11.3 and mid-2015 illumos, the `getrandom` syscall is available. +//! To make sure we can compile on both Solaris and its derivatives, as well as +//! function, we check for the existence of getrandom(2) in libc by calling +//! libc::dlsym. +use crate::util_libc::{sys_fill_exact, Weak}; +use crate::{use_file, Error}; +use core::mem; + +#[cfg(target_os = "illumos")] +type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t; +#[cfg(target_os = "solaris")] +type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::c_int; + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") }; + if let Some(fptr) = GETRANDOM.ptr() { + let func: GetRandomFn = unsafe { mem::transmute(fptr) }; + // 256 bytes is the lowest common denominator across all the Solaris + // derived platforms for atomically obtaining random data. + for chunk in dest.chunks_mut(256) { + sys_fill_exact(chunk, |buf| unsafe { + func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t + })? + } + Ok(()) + } else { + use_file::getrandom_inner(dest) + } +} diff --git a/getrandom/src/use_file.rs b/getrandom/src/use_file.rs new file mode 100644 index 0000000..d3adaf2 --- /dev/null +++ b/getrandom/src/use_file.rs @@ -0,0 +1,73 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementations that just need to read from a file +use crate::util_libc::{last_os_error, open_readonly, sys_fill_exact, LazyFd}; +use crate::Error; + +#[cfg(target_os = "redox")] +const FILE_PATH: &str = "rand:\0"; +#[cfg(any( + target_os = "dragonfly", + target_os = "emscripten", + target_os = "haiku", + target_os = "macos", + target_os = "solaris", + target_os = "illumos" +))] +const FILE_PATH: &str = "/dev/random\0"; + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + static FD: LazyFd = LazyFd::new(); + let fd = FD.init(init_file).ok_or_else(last_os_error)?; + let read = |buf: &mut [u8]| unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) }; + + if cfg!(target_os = "emscripten") { + // `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes. + for chunk in dest.chunks_mut(65536) { + sys_fill_exact(chunk, read)?; + } + } else { + sys_fill_exact(dest, read)?; + } + Ok(()) +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + fn init_file() -> Option<libc::c_int> { + // Poll /dev/random to make sure it is ok to read from /dev/urandom. + let mut pfd = libc::pollfd { + fd: unsafe { open_readonly("/dev/random\0")? }, + events: libc::POLLIN, + revents: 0, + }; + + let ret = loop { + // A negative timeout means an infinite timeout. + let res = unsafe { libc::poll(&mut pfd, 1, -1) }; + if res == 1 { + break unsafe { open_readonly("/dev/urandom\0") }; + } else if res < 0 { + let e = last_os_error().raw_os_error(); + if e == Some(libc::EINTR) || e == Some(libc::EAGAIN) { + continue; + } + } + // We either hard failed, or poll() returned the wrong pfd. + break None; + }; + unsafe { libc::close(pfd.fd) }; + ret + } + } else { + fn init_file() -> Option<libc::c_int> { + unsafe { open_readonly(FILE_PATH) } + } + } +} diff --git a/getrandom/src/util.rs b/getrandom/src/util.rs new file mode 100644 index 0000000..e0e9307 --- /dev/null +++ b/getrandom/src/util.rs @@ -0,0 +1,96 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + +// This structure represents a lazily initialized static usize value. Useful +// when it is preferable to just rerun initialization instead of locking. +// Both unsync_init and sync_init will invoke an init() function until it +// succeeds, then return the cached value for future calls. +// +// Both methods support init() "failing". If the init() method returns UNINIT, +// that value will be returned as normal, but will not be cached. +// +// Users should only depend on the _value_ returned by init() functions. +// Specifically, for the following init() function: +// fn init() -> usize { +// a(); +// let v = b(); +// c(); +// v +// } +// the effects of c() or writes to shared memory will not necessarily be +// observed and additional synchronization methods with be needed. +pub struct LazyUsize(AtomicUsize); + +impl LazyUsize { + pub const fn new() -> Self { + Self(AtomicUsize::new(Self::UNINIT)) + } + + // The initialization is not completed. + pub const UNINIT: usize = usize::max_value(); + // The initialization is currently running. + pub const ACTIVE: usize = usize::max_value() - 1; + + // Runs the init() function at least once, returning the value of some run + // of init(). Multiple callers can run their init() functions in parallel. + // init() should always return the same value, if it succeeds. + pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize { + // Relaxed ordering is fine, as we only have a single atomic variable. + let mut val = self.0.load(Relaxed); + if val == Self::UNINIT { + val = init(); + self.0.store(val, Relaxed); + } + val + } + + // Synchronously runs the init() function. Only one caller will have their + // init() function running at a time, and exactly one successful call will + // be run. init() returning UNINIT or ACTIVE will be considered a failure, + // and future calls to sync_init will rerun their init() function. + pub fn sync_init(&self, init: impl FnOnce() -> usize, mut wait: impl FnMut()) -> usize { + // Common and fast path with no contention. Don't wast time on CAS. + match self.0.load(Relaxed) { + Self::UNINIT | Self::ACTIVE => {} + val => return val, + } + // Relaxed ordering is fine, as we only have a single atomic variable. + loop { + match self.0.compare_and_swap(Self::UNINIT, Self::ACTIVE, Relaxed) { + Self::UNINIT => { + let val = init(); + self.0.store( + match val { + Self::UNINIT | Self::ACTIVE => Self::UNINIT, + val => val, + }, + Relaxed, + ); + return val; + } + Self::ACTIVE => wait(), + val => return val, + } + } + } +} + +// Identical to LazyUsize except with bool instead of usize. +pub struct LazyBool(LazyUsize); + +impl LazyBool { + pub const fn new() -> Self { + Self(LazyUsize::new()) + } + + pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool { + self.0.unsync_init(|| init() as usize) != 0 + } +} diff --git a/getrandom/src/util_libc.rs b/getrandom/src/util_libc.rs new file mode 100644 index 0000000..5a05170 --- /dev/null +++ b/getrandom/src/util_libc.rs @@ -0,0 +1,143 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use crate::error::ERRNO_NOT_POSITIVE; +use crate::util::LazyUsize; +use crate::Error; +use core::num::NonZeroU32; +use core::ptr::NonNull; + +cfg_if! { + if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] { + use libc::__errno as errno_location; + } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] { + use libc::__errno_location as errno_location; + } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + use libc::___errno as errno_location; + } else if #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))] { + use libc::__error as errno_location; + } else if #[cfg(target_os = "haiku")] { + use libc::_errnop as errno_location; + } +} + +pub fn last_os_error() -> Error { + #[cfg(not(target_os = "vxworks"))] + let errno = unsafe { *errno_location() }; + #[cfg(target_os = "vxworks")] + let errno = unsafe { libc::errnoGet() }; + if errno > 0 { + Error::from(NonZeroU32::new(errno as u32).unwrap()) + } else { + ERRNO_NOT_POSITIVE + } +} + +// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function: +// - should return -1 and set errno on failure +// - should return the number of bytes written on success +pub fn sys_fill_exact( + mut buf: &mut [u8], + sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t, +) -> Result<(), Error> { + while !buf.is_empty() { + let res = sys_fill(buf); + if res < 0 { + let err = last_os_error(); + // We should try again if the call was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err); + } + } else { + // We don't check for EOF (ret = 0) as the data we are reading + // should be an infinite stream of random bytes. + buf = &mut buf[(res as usize)..]; + } + } + Ok(()) +} + +// A "weak" binding to a C function that may or may not be present at runtime. +// Used for supporting newer OS features while still building on older systems. +// F must be a function pointer of type `unsafe extern "C" fn`. Based off of the +// weak! macro in libstd. +pub struct Weak { + name: &'static str, + addr: LazyUsize, +} + +impl Weak { + // Construct a binding to a C function with a given name. This function is + // unsafe because `name` _must_ be null terminated. + pub const unsafe fn new(name: &'static str) -> Self { + Self { + name, + addr: LazyUsize::new(), + } + } + + // Return a function pointer if present at runtime. Otherwise, return null. + pub fn ptr(&self) -> Option<NonNull<libc::c_void>> { + let addr = self.addr.unsync_init(|| unsafe { + libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize + }); + NonNull::new(addr as *mut _) + } +} + +pub struct LazyFd(LazyUsize); + +impl LazyFd { + pub const fn new() -> Self { + Self(LazyUsize::new()) + } + + // If init() returns Some(x), x should be nonnegative. + pub fn init(&self, init: impl FnOnce() -> Option<libc::c_int>) -> Option<libc::c_int> { + let fd = self.0.sync_init( + || match init() { + // OK as val >= 0 and val <= c_int::MAX < usize::MAX + Some(val) => val as usize, + None => LazyUsize::UNINIT, + }, + || unsafe { + // We are usually waiting on an open(2) syscall to complete, + // which typically takes < 10us if the file is a device. + // However, we might end up waiting much longer if the entropy + // pool isn't initialized, but even in that case, this loop will + // consume a negligible amount of CPU on most platforms. + libc::usleep(10); + }, + ); + match fd { + LazyUsize::UNINIT => None, + val => Some(val as libc::c_int), + } + } +} + +cfg_if! { + if #[cfg(any(target_os = "linux", target_os = "emscripten"))] { + use libc::open64 as open; + } else { + use libc::open; + } +} + +// SAFETY: path must be null terminated, FD must be manually closed. +pub unsafe fn open_readonly(path: &str) -> Option<libc::c_int> { + debug_assert!(path.as_bytes().last() == Some(&0)); + let fd = open(path.as_ptr() as *mut _, libc::O_RDONLY | libc::O_CLOEXEC); + if fd < 0 { + return None; + } + // O_CLOEXEC works on all Unix targets except for older Linux kernels (pre + // 2.6.23), so we also use an ioctl to make sure FD_CLOEXEC is set. + #[cfg(target_os = "linux")] + libc::ioctl(fd, libc::FIOCLEX); + Some(fd) +} diff --git a/getrandom/src/vxworks.rs b/getrandom/src/vxworks.rs new file mode 100644 index 0000000..a2fe52a --- /dev/null +++ b/getrandom/src/vxworks.rs @@ -0,0 +1,35 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for VxWorks +use crate::error::{Error, RAND_SECURE_FATAL}; +use crate::util_libc::last_os_error; +use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + static RNG_INIT: AtomicBool = AtomicBool::new(false); + while !RNG_INIT.load(Relaxed) { + let ret = unsafe { libc::randSecure() }; + if ret < 0 { + return Err(RAND_SECURE_FATAL); + } else if ret > 0 { + RNG_INIT.store(true, Relaxed); + break; + } + unsafe { libc::usleep(10) }; + } + + // Prevent overflow of i32 + for chunk in dest.chunks_mut(i32::max_value() as usize) { + let ret = unsafe { libc::randABytes(chunk.as_mut_ptr(), chunk.len() as i32) }; + if ret != 0 { + return Err(last_os_error()); + } + } + Ok(()) +} diff --git a/getrandom/src/wasi.rs b/getrandom/src/wasi.rs new file mode 100644 index 0000000..713c1ab --- /dev/null +++ b/getrandom/src/wasi.rs @@ -0,0 +1,19 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for WASI +use crate::Error; +use core::num; +use wasi::wasi_unstable::random_get; + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + random_get(dest).map_err(|e: num::NonZeroU16| { + // convert wasi's NonZeroU16 error into getrandom's NonZeroU32 error + num::NonZeroU32::new(e.get() as u32).unwrap().into() + }) +} diff --git a/getrandom/src/wasm32_bindgen.rs b/getrandom/src/wasm32_bindgen.rs new file mode 100644 index 0000000..86839a0 --- /dev/null +++ b/getrandom/src/wasm32_bindgen.rs @@ -0,0 +1,113 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for WASM via wasm-bindgen +extern crate std; + +use core::cell::RefCell; +use core::mem; +use std::thread_local; + +use wasm_bindgen::prelude::*; + +use crate::error::{BINDGEN_CRYPTO_UNDEF, BINDGEN_GRV_UNDEF}; +use crate::Error; + +#[derive(Clone, Debug)] +enum RngSource { + Node(NodeCrypto), + Browser(BrowserCrypto), +} + +// JsValues are always per-thread, so we initialize RngSource for each thread. +// See: https://github.com/rustwasm/wasm-bindgen/pull/955 +thread_local!( + static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None); +); + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + assert_eq!(mem::size_of::<usize>(), 4); + + RNG_SOURCE.with(|f| { + let mut source = f.borrow_mut(); + if source.is_none() { + *source = Some(getrandom_init()?); + } + + match source.as_ref().unwrap() { + RngSource::Node(n) => n.random_fill_sync(dest), + RngSource::Browser(n) => { + // see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues + // + // where it says: + // + // > A QuotaExceededError DOMException is thrown if the + // > requested length is greater than 65536 bytes. + for chunk in dest.chunks_mut(65536) { + n.get_random_values(chunk) + } + } + }; + Ok(()) + }) +} + +fn getrandom_init() -> Result<RngSource, Error> { + if let Ok(self_) = Global::get_self() { + // If `self` is defined then we're in a browser somehow (main window + // or web worker). Here we want to try to use + // `crypto.getRandomValues`, but if `crypto` isn't defined we assume + // we're in an older web browser and the OS RNG isn't available. + + let crypto = self_.crypto(); + if crypto.is_undefined() { + return Err(BINDGEN_CRYPTO_UNDEF); + } + + // Test if `crypto.getRandomValues` is undefined as well + let crypto: BrowserCrypto = crypto.into(); + if crypto.get_random_values_fn().is_undefined() { + return Err(BINDGEN_GRV_UNDEF); + } + + return Ok(RngSource::Browser(crypto)); + } + + return Ok(RngSource::Node(node_require("crypto"))); +} + +#[wasm_bindgen] +extern "C" { + type Global; + #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)] + fn get_self() -> Result<Self_, JsValue>; + + type Self_; + #[wasm_bindgen(method, getter, structural)] + fn crypto(me: &Self_) -> JsValue; + + #[derive(Clone, Debug)] + type BrowserCrypto; + + // TODO: these `structural` annotations here ideally wouldn't be here to + // avoid a JS shim, but for now with feature detection they're + // unavoidable. + #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)] + fn get_random_values_fn(me: &BrowserCrypto) -> JsValue; + #[wasm_bindgen(method, js_name = getRandomValues, structural)] + fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]); + + #[wasm_bindgen(js_name = require)] + fn node_require(s: &str) -> NodeCrypto; + + #[derive(Clone, Debug)] + type NodeCrypto; + + #[wasm_bindgen(method, js_name = randomFillSync, structural)] + fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]); +} diff --git a/getrandom/src/wasm32_stdweb.rs b/getrandom/src/wasm32_stdweb.rs new file mode 100644 index 0000000..6e5e78a --- /dev/null +++ b/getrandom/src/wasm32_stdweb.rs @@ -0,0 +1,114 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for WASM via stdweb +extern crate std; + +use core::mem; + +use stdweb::js; +use stdweb::unstable::TryInto; +use stdweb::web::error::Error as WebError; + +use crate::error::{STDWEB_NO_RNG, STDWEB_RNG_FAILED}; +use crate::Error; +use std::sync::Once; + +#[derive(Clone, Copy, Debug)] +enum RngSource { + Browser, + Node, +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + assert_eq!(mem::size_of::<usize>(), 4); + static ONCE: Once = Once::new(); + static mut RNG_SOURCE: Result<RngSource, Error> = Ok(RngSource::Node); + + // SAFETY: RNG_SOURCE is only written once, before being read. + ONCE.call_once(|| unsafe { + RNG_SOURCE = getrandom_init(); + }); + getrandom_fill(unsafe { RNG_SOURCE }?, dest) +} + +fn getrandom_init() -> Result<RngSource, Error> { + let result = js! { + try { + if ( + typeof self === "object" && + typeof self.crypto === "object" && + typeof self.crypto.getRandomValues === "function" + ) { + return { success: true, ty: 1 }; + } + + if (typeof require("crypto").randomBytes === "function") { + return { success: true, ty: 2 }; + } + + return { success: false, error: new Error("not supported") }; + } catch(err) { + return { success: false, error: err }; + } + }; + + if js! { return @{ result.as_ref() }.success } == true { + let ty = js! { return @{ result }.ty }; + + if ty == 1 { + Ok(RngSource::Browser) + } else if ty == 2 { + Ok(RngSource::Node) + } else { + unreachable!() + } + } else { + let _err: WebError = js! { return @{ result }.error }.try_into().unwrap(); + error!("getrandom unavailable: {}", _err); + Err(STDWEB_NO_RNG) + } +} + +fn getrandom_fill(source: RngSource, dest: &mut [u8]) -> Result<(), Error> { + for chunk in dest.chunks_mut(65536) { + let len = chunk.len() as u32; + let ptr = chunk.as_mut_ptr() as i32; + + let result = match source { + RngSource::Browser => js! { + try { + let array = new Uint8Array(@{ len }); + self.crypto.getRandomValues(array); + HEAPU8.set(array, @{ ptr }); + + return { success: true }; + } catch(err) { + return { success: false, error: err }; + } + }, + RngSource::Node => js! { + try { + let bytes = require("crypto").randomBytes(@{ len }); + HEAPU8.set(new Uint8Array(bytes), @{ ptr }); + + return { success: true }; + } catch(err) { + return { success: false, error: err }; + } + }, + }; + + if js! { return @{ result.as_ref() }.success } != true { + let _err: WebError = js! { return @{ result }.error }.try_into().unwrap(); + error!("getrandom failed: {}", _err); + return Err(STDWEB_RNG_FAILED); + } + } + Ok(()) +} diff --git a/getrandom/src/windows.rs b/getrandom/src/windows.rs new file mode 100644 index 0000000..e1b8df6 --- /dev/null +++ b/getrandom/src/windows.rs @@ -0,0 +1,26 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Windows +use crate::{error::RTL_GEN_RANDOM_FAILED, Error}; + +extern "system" { + #[link_name = "SystemFunction036"] + fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: u32) -> u8; +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + // Prevent overflow of u32 + for chunk in dest.chunks_mut(u32::max_value() as usize) { + let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr(), chunk.len() as u32) }; + if ret == 0 { + return Err(RTL_GEN_RANDOM_FAILED); + } + } + Ok(()) +} diff --git a/getrandom/src/windows_uwp.rs b/getrandom/src/windows_uwp.rs new file mode 100644 index 0000000..586c6f6 --- /dev/null +++ b/getrandom/src/windows_uwp.rs @@ -0,0 +1,59 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Windows UWP targets. After deprecation of Windows XP +//! and Vista, this can supersede the `RtlGenRandom`-based implementation. +use crate::Error; +use core::{ffi::c_void, num::NonZeroU32, ptr}; + +const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002; + +extern "system" { + fn BCryptGenRandom( + hAlgorithm: *mut c_void, + pBuffer: *mut u8, + cbBuffer: u32, + dwFlags: u32, + ) -> u32; +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + // Prevent overflow of u32 + for chunk in dest.chunks_mut(u32::max_value() as usize) { + let ret = unsafe { + BCryptGenRandom( + ptr::null_mut(), + chunk.as_mut_ptr(), + chunk.len() as u32, + BCRYPT_USE_SYSTEM_PREFERRED_RNG, + ) + }; + // NTSTATUS codes use two highest bits for severity status + match ret >> 30 { + 0b01 => { + info!("BCryptGenRandom: information code 0x{:08X}", ret); + } + 0b10 => { + warn!("BCryptGenRandom: warning code 0x{:08X}", ret); + } + 0b11 => { + error!("BCryptGenRandom: failed with 0x{:08X}", ret); + // We zeroize the highest bit, so the error code will reside + // inside the range of designated for OS codes. + let code = ret ^ (1 << 31); + // SAFETY: the second highest bit is always equal to one, + // so it's impossible to get zero. Unfortunately compiler + // is not smart enough to figure out it yet. + let code = unsafe { NonZeroU32::new_unchecked(code) }; + return Err(Error::from(code)); + } + _ => (), + } + } + Ok(()) +} diff --git a/getrandom/tests/common.rs b/getrandom/tests/common.rs new file mode 100644 index 0000000..afefa03 --- /dev/null +++ b/getrandom/tests/common.rs @@ -0,0 +1,68 @@ +#[cfg(feature = "wasm-bindgen")] +use wasm_bindgen_test::*; + +use getrandom::getrandom; + +#[cfg(feature = "test-in-browser")] +wasm_bindgen_test_configure!(run_in_browser); + +#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)] +#[test] +fn test_zero() { + // Test that APIs are happy with zero-length requests + getrandom(&mut [0u8; 0]).unwrap(); +} + +#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)] +#[test] +fn test_diff() { + let mut v1 = [0u8; 1000]; + getrandom(&mut v1).unwrap(); + + let mut v2 = [0u8; 1000]; + getrandom(&mut v2).unwrap(); + + let mut n_diff_bits = 0; + for i in 0..v1.len() { + n_diff_bits += (v1[i] ^ v2[i]).count_ones(); + } + + // Check at least 1 bit per byte differs. p(failure) < 1e-1000 with random input. + assert!(n_diff_bits >= v1.len() as u32); +} + +#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)] +#[test] +fn test_huge() { + let mut huge = [0u8; 100_000]; + getrandom(&mut huge).unwrap(); +} + +#[cfg(any(unix, windows, target_os = "redox", target_os = "fuchsia"))] +#[test] +fn test_multithreading() { + use std::sync::mpsc::channel; + use std::thread; + + let mut txs = vec![]; + for _ in 0..20 { + let (tx, rx) = channel(); + txs.push(tx); + + thread::spawn(move || { + // wait until all the tasks are ready to go. + rx.recv().unwrap(); + let mut v = [0u8; 1000]; + + for _ in 0..100 { + getrandom(&mut v).unwrap(); + thread::yield_now(); + } + }); + } + + // start all the tasks + for tx in txs.iter() { + tx.send(()).unwrap(); + } +} diff --git a/getrandom/utils/ci/install.sh b/getrandom/utils/ci/install.sh new file mode 100644 index 0000000..8e636e1 --- /dev/null +++ b/getrandom/utils/ci/install.sh @@ -0,0 +1,49 @@ +# From https://github.com/japaric/trust + +set -ex + +main() { + local target= + if [ $TRAVIS_OS_NAME = linux ]; then + target=x86_64-unknown-linux-musl + sort=sort + else + target=x86_64-apple-darwin + sort=gsort # for `sort --sort-version`, from brew's coreutils. + fi + + # Builds for iOS are done on OSX, but require the specific target to be + # installed. + case $TARGET in + aarch64-apple-ios) + rustup target install aarch64-apple-ios + ;; + armv7-apple-ios) + rustup target install armv7-apple-ios + ;; + armv7s-apple-ios) + rustup target install armv7s-apple-ios + ;; + i386-apple-ios) + rustup target install i386-apple-ios + ;; + x86_64-apple-ios) + rustup target install x86_64-apple-ios + ;; + esac + + # This fetches latest stable release + local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ + | cut -d/ -f3 \ + | grep -E '^v[0.1.0-9.]+$' \ + | $sort --version-sort \ + | tail -n1) + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- \ + --force \ + --git japaric/cross \ + --tag $tag \ + --target $target +} + +main diff --git a/getrandom/utils/ci/script.sh b/getrandom/utils/ci/script.sh new file mode 100644 index 0000000..b3e80d4 --- /dev/null +++ b/getrandom/utils/ci/script.sh @@ -0,0 +1,13 @@ +# Derived from https://github.com/japaric/trust + +set -ex + +main() { + cross test --target $TARGET + cross test --target $TARGET --examples +} + +# we don't run the "test phase" when doing deploys +if [ -z $TRAVIS_TAG ]; then + main +fi diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md index 105c42d..7039d57 100644 --- a/nitrocli/CHANGELOG.md +++ b/nitrocli/CHANGELOG.md @@ -1,9 +1,11 @@ Unreleased ---------- - Added note about interaction with GnuPG to `README` file -- Bumped `nitrokey` dependency to `0.4.0-alpha.3` +- Bumped `nitrokey` dependency to `0.4.0` - Bumped `nitrokey-sys` dependency to `3.5.0` - Added `lazy_static` dependency in version `1.4.0` + - Added `cfg-if` dependency in version `0.1.10` + - Added `getrandom` dependency in version `0.1.13` 0.3.0 diff --git a/nitrocli/Cargo.lock b/nitrocli/Cargo.lock index 32ae013..877caff 100644 --- a/nitrocli/Cargo.lock +++ b/nitrocli/Cargo.lock @@ -17,37 +17,23 @@ name = "base32" version = "0.4.0" [[package]] -name = "bitflags" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "cc" version = "1.0.48" [[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] +name = "cfg-if" +version = "0.1.10" [[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "getrandom" +version = "0.1.13" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc 0.2.66", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "lazy_static" version = "1.4.0" @@ -67,7 +53,7 @@ dependencies = [ "argparse 0.2.2", "base32 0.4.0", "libc 0.2.66", - "nitrokey 0.4.0-alpha.3", + "nitrokey 0.4.0", "nitrokey-test 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "nitrokey-test-state 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -75,13 +61,12 @@ dependencies = [ [[package]] name = "nitrokey" -version = "0.4.0-alpha.3" +version = "0.4.0" dependencies = [ "lazy_static 1.4.0", "libc 0.2.66", "nitrokey-sys 3.5.0", - "rand_core 0.3.0", - "rand_os 0.1.1", + "rand_core 0.5.1", ] [[package]] @@ -124,26 +109,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.3.0" - -[[package]] -name = "rand_os" -version = "0.1.1" +version = "0.5.1" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66", - "rand_core 0.3.0", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.0", + "getrandom 0.1.13", ] [[package]] @@ -186,41 +154,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "winapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "wasi" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum nitrokey-test 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f3da0c2cedaa512f79fbc3ed45143a52c76c5edcca88d0823b967ff11d05fe37" "checksum nitrokey-test-state 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a59b732ed6d5212424ed31ec9649f05652bcbc38f45f2292b27a6044e7098803" "checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum syn 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc157159e2a7df58cd67b1cace10b8ed256a404fb0070593f137d8ba6bef4de" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" diff --git a/nitrocli/Cargo.toml b/nitrocli/Cargo.toml index a0fa3ed..273b9c8 100644 --- a/nitrocli/Cargo.toml +++ b/nitrocli/Cargo.toml @@ -1,7 +1,7 @@ # Cargo.toml #/*************************************************************************** -# * Copyright (C) 2017-2019 Daniel Mueller (deso@posteo.net) * +# * Copyright (C) 2017-2020 Daniel Mueller (deso@posteo.net) * # * * # * This program is free software: you can redistribute it and/or modify * # * it under the terms of the GNU General Public License as published by * @@ -53,7 +53,7 @@ path = "../base32" version = "0.2" [dependencies.nitrokey] -version = "0.4.0-alpha.3" +version = "0.4.0" [dev-dependencies.nitrokey-test] version = "0.3.1" @@ -68,9 +68,10 @@ version = "1" argparse = { path = "../argparse" } base32 = { path = "../base32" } cc = { path = "../cc" } +cfg-if = { path = "../cfg-if" } +getrandom = { path = "../getrandom" } libc = { path = "../libc" } nitrokey = { path = "../nitrokey" } nitrokey-sys = { path = "../nitrokey-sys" } lazy_static = { path = "../lazy-static" } rand_core = { path = "../rand/rand_core" } -rand_os = { path = "../rand/rand_os" } diff --git a/nitrokey/.builds/archlinux-msrv.yml b/nitrokey/.builds/archlinux-msrv.yml new file mode 100644 index 0000000..66c0390 --- /dev/null +++ b/nitrokey/.builds/archlinux-msrv.yml @@ -0,0 +1,22 @@ +# Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> +# SPDX-License-Identifier: CC0-1.0 +image: archlinux +packages: + - rustup + - libnitrokey +environment: + USE_SYSTEM_LIBNITROKEY: "1" +sources: + - https://git.ireas.org/nitrokey-rs +tasks: + - setup: | + rustup set profile minimal + rustup default 1.34.2 + - version: | + rustc -V + - build: | + cd nitrokey-rs + cargo build --release + - test: | + cd nitrokey-rs + cargo test diff --git a/nitrokey/.builds/archlinux-use-system-lib.yml b/nitrokey/.builds/archlinux-use-system-lib.yml index ac0fc0f..29bda19 100644 --- a/nitrokey/.builds/archlinux-use-system-lib.yml +++ b/nitrokey/.builds/archlinux-use-system-lib.yml @@ -1,5 +1,5 @@ # Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 image: archlinux packages: - rust @@ -9,9 +9,17 @@ environment: sources: - https://git.ireas.org/nitrokey-rs tasks: + - version: | + rustc -V - build: | cd nitrokey-rs cargo build --release - test: | cd nitrokey-rs cargo test + - format: | + cd nitrokey-rs + cargo fmt -- --check + - clippy: | + cd nitrokey-rs + cargo clippy -- -D warnings diff --git a/nitrokey/.builds/archlinux.yml b/nitrokey/.builds/archlinux.yml index dfe2639..151eb66 100644 --- a/nitrokey/.builds/archlinux.yml +++ b/nitrokey/.builds/archlinux.yml @@ -1,5 +1,5 @@ # Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 image: archlinux packages: - rust diff --git a/nitrokey/.builds/lint.yml b/nitrokey/.builds/lint.yml index 86a27cd..678cde8 100644 --- a/nitrokey/.builds/lint.yml +++ b/nitrokey/.builds/lint.yml @@ -1,27 +1,16 @@ # Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> -# SPDX-License-Identifier: MIT +# SPDX-License-Identifier: CC0-1.0 image: archlinux packages: - - rustup - - python - - python-pip - - python-pygit2 + - gnupg + - reuse sources: - https://git.ireas.org/nitrokey-rs tasks: - - setup: | - pip install --user fsfe-reuse - rustup update stable - rustup self upgrade-data - rustup default stable - rustup component add rustfmt - rustup component add clippy - - format: | + - verify: | cd nitrokey-rs - cargo fmt -- --check + curl -s "https://pgp.ireas.org/0x6D533958F070C57C.txt" | gpg --import + git verify-commit HEAD - reuse: | cd nitrokey-rs - ~/.local/bin/reuse lint - - clippy: | - cd nitrokey-rs - cargo clippy -- -D warnings + reuse lint diff --git a/nitrokey/.gitignore b/nitrokey/.gitignore index 4cdf3b3..7ea18df 100644 --- a/nitrokey/.gitignore +++ b/nitrokey/.gitignore @@ -1,4 +1,5 @@ - +# Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> +# SPDX-License-Identifier: CC0-1.0 /target /nitrokey-sys/target **/*.rs.bk diff --git a/nitrokey/CHANGELOG.md b/nitrokey/CHANGELOG.md index 3051d0f..d4451bc 100644 --- a/nitrokey/CHANGELOG.md +++ b/nitrokey/CHANGELOG.md @@ -1,9 +1,9 @@ <!--- -Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> -SPDX-License-Identifier: MIT +Copyright (C) 2019-2020 Robin Krahl <robin.krahl@ireas.org> +SPDX-License-Identifier: CC0-1.0 --> -# Unreleased +# v0.4.0 (2020-01-02) - Remove the `test-pro` and `test-storage` features. - Implement `Display` for `Version`. - Introduce `DEFAULT_ADMIN_PIN` and `DEFAULT_USER_PIN` constants. @@ -38,9 +38,12 @@ SPDX-License-Identifier: MIT - Implement `DerefMut` for `User<T>` and `Admin<T>`. - Add `device_mut` method to `DeviceWrapper`. - Require a mutable `Device` reference if a method changes the device state. -- Update the `nitrokey-sys` dependency to version 3.5.0. -- Update the `nitrokey-test` dependency to version 0.3 and add the - `nitrokey-test-state` dependency in version 0.1.0. +- Update dependencies: + - `nitrokey-sys` to 3.5 + - `nitrokey-test` to 0.3 + - `rand_core` to 0.5 + - `rand_os` to 0.2 +- Add `nitrokey-test-state` dependency in version 0.1. - Refactor connection management: - Add `ConcurrentAccessError` and `PoisonError` `Error` variants. - Add the `Manager` struct that manages connections to Nitrokey devices. @@ -48,6 +51,10 @@ SPDX-License-Identifier: MIT - Add the `into_manager` function to the `Device` trait. - Add the `force_take` function that ignores a `PoisonError` when accessing the manager instance. +- Internally refactor the `device` module into submodules. + +# v0.3.5 (2019-12-16) +- Update the nitrokey-sys dependency version specification to ~3.4. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/nitrokey/Cargo.toml b/nitrokey/Cargo.toml index 62eea02..b57591b 100644 --- a/nitrokey/Cargo.toml +++ b/nitrokey/Cargo.toml @@ -1,9 +1,9 @@ -# Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> -# SPDX-License-Identifier: MIT +# Copyright (C) 2019-2020 Robin Krahl <robin.krahl@ireas.org> +# SPDX-License-Identifier: CC0-1.0 [package] name = "nitrokey" -version = "0.4.0-alpha.3" +version = "0.4.0" authors = ["Robin Krahl <robin.krahl@ireas.org>"] edition = "2018" homepage = "https://code.ireas.org/nitrokey-rs/" @@ -17,12 +17,11 @@ license = "MIT" exclude = [".builds/*"] [dependencies] -lazy_static = "1.2.0" +lazy_static = "1.2" libc = "0.2" nitrokey-sys = "3.5" -rand_core = {version = "0.3", default-features = false, features = ["std"] } -rand_os = {version = "0.1"} +rand_core = {version = "0.5.1", features = ["getrandom"] } [dev-dependencies] nitrokey-test = "0.3" -nitrokey-test-state = "0.1.0" +nitrokey-test-state = "0.1" diff --git a/nitrokey/LICENSES/CC0-1.0.txt b/nitrokey/LICENSES/CC0-1.0.txt new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/nitrokey/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/nitrokey/LICENSE b/nitrokey/LICENSES/MIT.txt index 6c67cd5..6c67cd5 100644 --- a/nitrokey/LICENSE +++ b/nitrokey/LICENSES/MIT.txt diff --git a/nitrokey/README.md b/nitrokey/README.md index a29ac6f..12a9f6d 100644 --- a/nitrokey/README.md +++ b/nitrokey/README.md @@ -1,6 +1,6 @@ <!--- Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> -SPDX-License-Identifier: MIT +SPDX-License-Identifier: CC0-1.0 --> # nitrokey-rs @@ -55,14 +55,18 @@ Note that the tests assume that the device’s passwords are the factory default (admin PIN `12345678`, user PIN `123456`, update password `12345678`) and that an AES key has been built. Some tests will overwrite the data stored on the Nitrokey device or perform a factory reset. Never execute the tests if you -unless yout want to destroy all data on all connected Nitrokey devices! +don’t want to destroy all data on any connected Nitrokey device! ## Acknowledgments -Thanks to Nitrokey UG for providing a Nitrokey Storage to support the +Thanks to Nitrokey UG for providing two Nitrokey devices to support the development of this crate. Thanks to Daniel Mueller for contributions to `nitrokey-rs` and for the `nitrokey-test` crate. +## Minimum Supported Rust Version + +This crate supports Rust 1.34.2 or later. + ## Contact For bug reports, patches, feature requests or other messages, please send a @@ -70,16 +74,19 @@ mail to [nitrokey-rs-dev@ireas.org][]. ## License -This project is licensed under the [MIT License][]. `libnitrokey` is licensed -under the [LGPL-3.0][]. +This project is licensed under the [MIT][] license. The documentation and +configuration files contained in this repository are licensed under the +[Creative Commons Zero][CC0] license. You can find a copy of the license texts +in the `LICENSES` directory. `libnitrokey` is licensed under the [LGPL-3.0][]. -`nitrokey-rs` complies with [version 2.0 of the REUSE practices][reuse]. +`nitrokey-rs` complies with [version 3.0 of the REUSE specification][reuse]. [Documentation]: https://docs.rs/nitrokey [Nitrokey udev rules]: https://www.nitrokey.com/documentation/frequently-asked-questions-faq#openpgp-card-not-available [`libnitrokey`]: https://github.com/nitrokey/libnitrokey [`nitrokey-test`]: https://github.com/d-e-s-o/nitrokey-test [nitrokey-rs-dev@ireas.org]: mailto:nitrokey-rs-dev@ireas.org -[MIT license]: https://opensource.org/licenses/MIT +[MIT]: https://opensource.org/licenses/MIT +[CC0]: https://creativecommons.org/publicdomain/zero/1.0/ [LGPL-3.0]: https://opensource.org/licenses/lgpl-3.0.html -[reuse]: https://reuse.software/practices/2.0/ +[reuse]: https://reuse.software/practices/3.0/ diff --git a/nitrokey/TODO.md b/nitrokey/TODO.md index efa66d3..54525ef 100644 --- a/nitrokey/TODO.md +++ b/nitrokey/TODO.md @@ -1,6 +1,6 @@ <!--- Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org> -SPDX-License-Identifier: MIT +SPDX-License-Identifier: CC0-1.0 --> - Add support for the currently unsupported commands: diff --git a/nitrokey/src/auth.rs b/nitrokey/src/auth.rs index 0b000f7..cab1021 100644 --- a/nitrokey/src/auth.rs +++ b/nitrokey/src/auth.rs @@ -1,6 +1,7 @@ // Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> // SPDX-License-Identifier: MIT +use std::convert::TryFrom as _; use std::marker; use std::ops; use std::os::raw::c_char; diff --git a/nitrokey/src/config.rs b/nitrokey/src/config.rs index c273792..cb678d7 100644 --- a/nitrokey/src/config.rs +++ b/nitrokey/src/config.rs @@ -1,6 +1,8 @@ // Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> // SPDX-License-Identifier: MIT +use std::convert; + use crate::error::{Error, LibraryError}; /// The configuration for a Nitrokey. @@ -68,8 +70,10 @@ impl Config { } } -impl RawConfig { - pub fn try_from(config: Config) -> Result<RawConfig, Error> { +impl convert::TryFrom<Config> for RawConfig { + type Error = Error; + + fn try_from(config: Config) -> Result<RawConfig, Error> { Ok(RawConfig { numlock: option_to_config_otp_slot(config.numlock)?, capslock: option_to_config_otp_slot(config.capslock)?, diff --git a/nitrokey/src/device/mod.rs b/nitrokey/src/device/mod.rs new file mode 100644 index 0000000..5e15f08 --- /dev/null +++ b/nitrokey/src/device/mod.rs @@ -0,0 +1,464 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +mod pro; +mod storage; +mod wrapper; + +use std::fmt; + +use libc; +use nitrokey_sys; + +use crate::auth::Authenticate; +use crate::config::{Config, RawConfig}; +use crate::error::{CommunicationError, Error}; +use crate::otp::GenerateOtp; +use crate::pws::GetPasswordSafe; +use crate::util::{ + get_command_result, get_cstring, get_last_error, result_from_string, result_or_error, +}; + +pub use pro::Pro; +pub use storage::{ + SdCardData, Storage, StorageProductionInfo, StorageStatus, VolumeMode, VolumeStatus, +}; +pub use wrapper::DeviceWrapper; + +/// Available Nitrokey models. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Model { + /// The Nitrokey Storage. + Storage, + /// The Nitrokey Pro. + Pro, +} + +impl fmt::Display for Model { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + Model::Pro => "Pro", + Model::Storage => "Storage", + }) + } +} + +/// A firmware version for a Nitrokey device. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct FirmwareVersion { + /// The major firmware version, e. g. 0 in v0.40. + pub major: u8, + /// The minor firmware version, e. g. 40 in v0.40. + pub minor: u8, +} + +impl fmt::Display for FirmwareVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "v{}.{}", self.major, self.minor) + } +} + +/// A Nitrokey device. +/// +/// This trait provides the commands that can be executed without authentication and that are +/// present on all supported Nitrokey devices. +pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug { + /// Returns the [`Manager`][] instance that has been used to connect to this device. + /// + /// # Example + /// + /// ``` + /// use nitrokey::{Device, DeviceWrapper}; + /// + /// fn do_something(device: DeviceWrapper) { + /// // reconnect to any device + /// let manager = device.into_manager(); + /// let device = manager.connect(); + /// // do something with the device + /// // ... + /// } + /// + /// match nitrokey::take()?.connect() { + /// Ok(device) => do_something(device), + /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), + /// } + /// # Ok::<(), nitrokey::Error>(()) + /// ``` + fn into_manager(self) -> &'a mut crate::Manager; + + /// Returns the model of the connected Nitrokey device. + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// println!("Connected to a Nitrokey {}", device.get_model()); + /// # Ok(()) + /// # } + fn get_model(&self) -> Model; + + /// Returns the serial number of the Nitrokey device. The serial number is the string + /// representation of a hex number. + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// match device.get_serial_number() { + /// Ok(number) => println!("serial no: {}", number), + /// Err(err) => eprintln!("Could not get serial number: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + fn get_serial_number(&self) -> Result<String, Error> { + result_from_string(unsafe { nitrokey_sys::NK_device_serial_number() }) + } + + /// Returns the number of remaining authentication attempts for the user. The total number of + /// available attempts is three. + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// let count = device.get_user_retry_count(); + /// match device.get_user_retry_count() { + /// Ok(count) => println!("{} remaining authentication attempts (user)", count), + /// Err(err) => eprintln!("Could not get user retry count: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + fn get_user_retry_count(&self) -> Result<u8, Error> { + result_or_error(unsafe { nitrokey_sys::NK_get_user_retry_count() }) + } + + /// Returns the number of remaining authentication attempts for the admin. The total number of + /// available attempts is three. + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// let count = device.get_admin_retry_count(); + /// match device.get_admin_retry_count() { + /// Ok(count) => println!("{} remaining authentication attempts (admin)", count), + /// Err(err) => eprintln!("Could not get admin retry count: {}", err), + /// } + /// # Ok(()) + /// # } + /// ``` + fn get_admin_retry_count(&self) -> Result<u8, Error> { + result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() }) + } + + /// Returns the firmware version. + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// match device.get_firmware_version() { + /// Ok(version) => println!("Firmware version: {}", version), + /// Err(err) => eprintln!("Could not access firmware version: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + fn get_firmware_version(&self) -> Result<FirmwareVersion, Error> { + let major = result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() })?; + let minor = result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() })?; + Ok(FirmwareVersion { major, minor }) + } + + /// Returns the current configuration of the Nitrokey device. + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let device = manager.connect()?; + /// let config = device.get_config()?; + /// println!("numlock binding: {:?}", config.numlock); + /// println!("capslock binding: {:?}", config.capslock); + /// println!("scrollock binding: {:?}", config.scrollock); + /// println!("require password for OTP: {:?}", config.user_password); + /// # Ok(()) + /// # } + /// ``` + fn get_config(&self) -> Result<Config, Error> { + let config_ptr = unsafe { nitrokey_sys::NK_read_config() }; + if config_ptr.is_null() { + return Err(get_last_error()); + } + let config_array_ptr = config_ptr as *const [u8; 5]; + let raw_config = unsafe { RawConfig::from(*config_array_ptr) }; + unsafe { libc::free(config_ptr as *mut libc::c_void) }; + Ok(raw_config.into()) + } + + /// Changes the administrator PIN. + /// + /// # Errors + /// + /// - [`InvalidString`][] if one of the provided passwords contains a null byte + /// - [`WrongPassword`][] if the current admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; + /// match device.change_admin_pin("12345678", "12345679") { + /// Ok(()) => println!("Updated admin PIN."), + /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + fn change_admin_pin(&mut self, current: &str, new: &str) -> Result<(), Error> { + let current_string = get_cstring(current)?; + let new_string = get_cstring(new)?; + get_command_result(unsafe { + nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr()) + }) + } + + /// Changes the user PIN. + /// + /// # Errors + /// + /// - [`InvalidString`][] if one of the provided passwords contains a null byte + /// - [`WrongPassword`][] if the current user password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; + /// match device.change_user_pin("123456", "123457") { + /// Ok(()) => println!("Updated admin PIN."), + /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + fn change_user_pin(&mut self, current: &str, new: &str) -> Result<(), Error> { + let current_string = get_cstring(current)?; + let new_string = get_cstring(new)?; + get_command_result(unsafe { + nitrokey_sys::NK_change_user_PIN(current_string.as_ptr(), new_string.as_ptr()) + }) + } + + /// Unlocks the user PIN after three failed login attempts and sets it to the given value. + /// + /// # Errors + /// + /// - [`InvalidString`][] if one of the provided passwords contains a null byte + /// - [`WrongPassword`][] if the admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; + /// match device.unlock_user_pin("12345678", "123456") { + /// Ok(()) => println!("Unlocked user PIN."), + /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString + /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword + fn unlock_user_pin(&mut self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { + let admin_pin_string = get_cstring(admin_pin)?; + let user_pin_string = get_cstring(user_pin)?; + get_command_result(unsafe { + nitrokey_sys::NK_unlock_user_password( + admin_pin_string.as_ptr(), + user_pin_string.as_ptr(), + ) + }) + } + + /// Locks the Nitrokey device. + /// + /// This disables the password store if it has been unlocked. On the Nitrokey Storage, this + /// also disables the volumes if they have been enabled. + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; + /// match device.lock() { + /// Ok(()) => println!("Locked the Nitrokey device."), + /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + fn lock(&mut self) -> Result<(), Error> { + get_command_result(unsafe { nitrokey_sys::NK_lock_device() }) + } + + /// Performs a factory reset on the Nitrokey device. + /// + /// This commands performs a factory reset on the smart card (like the factory reset via `gpg + /// --card-edit`) and then clears the flash memory (password safe, one-time passwords etc.). + /// After a factory reset, [`build_aes_key`][] has to be called before the password safe or the + /// encrypted volume can be used. + /// + /// # Errors + /// + /// - [`InvalidString`][] if the provided password contains a null byte + /// - [`WrongPassword`][] if the admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; + /// match device.factory_reset("12345678") { + /// Ok(()) => println!("Performed a factory reset."), + /// Err(err) => eprintln!("Could not perform a factory reset: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`build_aes_key`]: #method.build_aes_key + fn factory_reset(&mut self, admin_pin: &str) -> Result<(), Error> { + let admin_pin_string = get_cstring(admin_pin)?; + get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) }) + } + + /// Builds a new AES key on the Nitrokey. + /// + /// The AES key is used to encrypt the password safe and the encrypted volume. You may need + /// to call this method after a factory reset, either using [`factory_reset`][] or using `gpg + /// --card-edit`. You can also use it to destroy the data stored in the password safe or on + /// the encrypted volume. + /// + /// # Errors + /// + /// - [`InvalidString`][] if the provided password contains a null byte + /// - [`WrongPassword`][] if the admin password is wrong + /// + /// # Example + /// + /// ```no_run + /// use nitrokey::Device; + /// # use nitrokey::Error; + /// + /// # fn try_main() -> Result<(), Error> { + /// let mut manager = nitrokey::take()?; + /// let mut device = manager.connect()?; + /// match device.build_aes_key("12345678") { + /// Ok(()) => println!("New AES keys have been built."), + /// Err(err) => eprintln!("Could not build new AES keys: {}", err), + /// }; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`factory_reset`]: #method.factory_reset + fn build_aes_key(&mut self, admin_pin: &str) -> Result<(), Error> { + let admin_pin_string = get_cstring(admin_pin)?; + get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) }) + } +} + +fn get_connected_model() -> Option<Model> { + match unsafe { nitrokey_sys::NK_get_device_model() } { + nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), + nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage), + _ => None, + } +} + +pub(crate) fn create_device_wrapper( + manager: &mut crate::Manager, + model: Model, +) -> DeviceWrapper<'_> { + match model { + Model::Pro => Pro::new(manager).into(), + Model::Storage => Storage::new(manager).into(), + } +} + +pub(crate) fn get_connected_device( + manager: &mut crate::Manager, +) -> Result<DeviceWrapper<'_>, Error> { + match get_connected_model() { + Some(model) => Ok(create_device_wrapper(manager, model)), + None => Err(CommunicationError::NotConnected.into()), + } +} + +pub(crate) fn connect_enum(model: Model) -> bool { + let model = match model { + Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE, + Model::Pro => nitrokey_sys::NK_device_model_NK_PRO, + }; + unsafe { nitrokey_sys::NK_login_enum(model) == 1 } +} diff --git a/nitrokey/src/device/pro.rs b/nitrokey/src/device/pro.rs new file mode 100644 index 0000000..a65345e --- /dev/null +++ b/nitrokey/src/device/pro.rs @@ -0,0 +1,79 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +use nitrokey_sys; + +use crate::device::{Device, Model}; +use crate::otp::GenerateOtp; + +/// A Nitrokey Pro device without user or admin authentication. +/// +/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_pro`] method to +/// directly obtain an instance. If you want to execute a command that requires user or admin +/// authentication, use [`authenticate_admin`][] or [`authenticate_user`][]. +/// +/// # Examples +/// +/// Authentication with error handling: +/// +/// ```no_run +/// use nitrokey::{Authenticate, User, Pro}; +/// # use nitrokey::Error; +/// +/// fn perform_user_task<'a>(device: &User<'a, Pro<'a>>) {} +/// fn perform_other_task(device: &Pro) {} +/// +/// # fn try_main() -> Result<(), Error> { +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect_pro()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, +/// }; +/// perform_other_task(&device); +/// # Ok(()) +/// # } +/// ``` +/// +/// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin +/// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user +/// [`connect`]: struct.Manager.html#method.connect +/// [`connect_pro`]: struct.Manager.html#method.connect_pro +#[derive(Debug)] +pub struct Pro<'a> { + manager: Option<&'a mut crate::Manager>, +} + +impl<'a> Pro<'a> { + pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { + Pro { + manager: Some(manager), + } + } +} + +impl<'a> Drop for Pro<'a> { + fn drop(&mut self) { + unsafe { + nitrokey_sys::NK_logout(); + } + } +} + +impl<'a> Device<'a> for Pro<'a> { + fn into_manager(mut self) -> &'a mut crate::Manager { + self.manager.take().unwrap() + } + + fn get_model(&self) -> Model { + Model::Pro + } +} + +impl<'a> GenerateOtp for Pro<'a> {} diff --git a/nitrokey/src/device.rs b/nitrokey/src/device/storage.rs index 758d4c1..370ce36 100644 --- a/nitrokey/src/device.rs +++ b/nitrokey/src/device/storage.rs @@ -3,163 +3,12 @@ use std::fmt; -use libc; use nitrokey_sys; -use crate::auth::Authenticate; -use crate::config::{Config, RawConfig}; -use crate::error::{CommunicationError, Error}; +use crate::device::{Device, FirmwareVersion, Model}; +use crate::error::Error; use crate::otp::GenerateOtp; -use crate::pws::GetPasswordSafe; -use crate::util::{ - get_command_result, get_cstring, get_last_error, result_from_string, result_or_error, -}; - -/// Available Nitrokey models. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Model { - /// The Nitrokey Storage. - Storage, - /// The Nitrokey Pro. - Pro, -} - -impl fmt::Display for Model { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match *self { - Model::Pro => "Pro", - Model::Storage => "Storage", - }) - } -} - -/// The access mode of a volume on the Nitrokey Storage. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum VolumeMode { - /// A read-only volume. - ReadOnly, - /// A read-write volume. - ReadWrite, -} - -impl fmt::Display for VolumeMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match *self { - VolumeMode::ReadOnly => "read-only", - VolumeMode::ReadWrite => "read-write", - }) - } -} - -/// A wrapper for a Nitrokey device of unknown type. -/// -/// Use the [`connect`][] method to obtain a wrapped instance. The wrapper implements all traits -/// that are shared between all Nitrokey devices so that the shared functionality can be used -/// without knowing the type of the underlying device. If you want to use functionality that is -/// not available for all devices, you have to extract the device. -/// -/// # Examples -/// -/// Authentication with error handling: -/// -/// ```no_run -/// use nitrokey::{Authenticate, DeviceWrapper, User}; -/// # use nitrokey::Error; -/// -/// fn perform_user_task<'a>(device: &User<'a, DeviceWrapper<'a>>) {} -/// fn perform_other_task(device: &DeviceWrapper) {} -/// -/// # fn try_main() -> Result<(), Error> { -/// let mut manager = nitrokey::take()?; -/// let device = manager.connect()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, -/// }; -/// perform_other_task(&device); -/// # Ok(()) -/// # } -/// ``` -/// -/// Device-specific commands: -/// -/// ```no_run -/// use nitrokey::{DeviceWrapper, Storage}; -/// # use nitrokey::Error; -/// -/// fn perform_common_task(device: &DeviceWrapper) {} -/// fn perform_storage_task(device: &Storage) {} -/// -/// # fn try_main() -> Result<(), Error> { -/// let mut manager = nitrokey::take()?; -/// let device = manager.connect()?; -/// perform_common_task(&device); -/// match device { -/// DeviceWrapper::Storage(storage) => perform_storage_task(&storage), -/// _ => (), -/// }; -/// # Ok(()) -/// # } -/// ``` -/// -/// [`connect`]: struct.Manager.html#method.connect -#[derive(Debug)] -pub enum DeviceWrapper<'a> { - /// A Nitrokey Storage device. - Storage(Storage<'a>), - /// A Nitrokey Pro device. - Pro(Pro<'a>), -} - -/// A Nitrokey Pro device without user or admin authentication. -/// -/// Use the [`connect`][] method to obtain an instance wrapper or the [`connect_pro`] method to -/// directly obtain an instance. If you want to execute a command that requires user or admin -/// authentication, use [`authenticate_admin`][] or [`authenticate_user`][]. -/// -/// # Examples -/// -/// Authentication with error handling: -/// -/// ```no_run -/// use nitrokey::{Authenticate, User, Pro}; -/// # use nitrokey::Error; -/// -/// fn perform_user_task<'a>(device: &User<'a, Pro<'a>>) {} -/// fn perform_other_task(device: &Pro) {} -/// -/// # fn try_main() -> Result<(), Error> { -/// let mut manager = nitrokey::take()?; -/// let device = manager.connect_pro()?; -/// let device = match device.authenticate_user("123456") { -/// Ok(user) => { -/// perform_user_task(&user); -/// user.device() -/// }, -/// Err((device, err)) => { -/// eprintln!("Could not authenticate as user: {}", err); -/// device -/// }, -/// }; -/// perform_other_task(&device); -/// # Ok(()) -/// # } -/// ``` -/// -/// [`authenticate_admin`]: trait.Authenticate.html#method.authenticate_admin -/// [`authenticate_user`]: trait.Authenticate.html#method.authenticate_user -/// [`connect`]: struct.Manager.html#method.connect -/// [`connect_pro`]: struct.Manager.html#method.connect_pro -#[derive(Debug)] -pub struct Pro<'a> { - manager: Option<&'a mut crate::Manager>, -} +use crate::util::{get_command_result, get_cstring}; /// A Nitrokey Storage device without user or admin authentication. /// @@ -205,6 +54,24 @@ pub struct Storage<'a> { manager: Option<&'a mut crate::Manager>, } +/// The access mode of a volume on the Nitrokey Storage. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum VolumeMode { + /// A read-only volume. + ReadOnly, + /// A read-write volume. + ReadWrite, +} + +impl fmt::Display for VolumeMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + VolumeMode::ReadOnly => "read-only", + VolumeMode::ReadWrite => "read-write", + }) + } +} + /// The status of a volume on a Nitrokey Storage device. #[derive(Debug)] pub struct VolumeStatus { @@ -231,21 +98,6 @@ pub struct SdCardData { pub manufacturer: u8, } -/// A firmware version for a Nitrokey device. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct FirmwareVersion { - /// The major firmware version, e. g. 0 in v0.40. - pub major: u8, - /// The minor firmware version, e. g. 40 in v0.40. - pub minor: u8, -} - -impl fmt::Display for FirmwareVersion { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "v{}.{}", self.major, self.minor) - } -} - /// Production information for a Storage device. #[derive(Debug)] pub struct StorageProductionInfo { @@ -289,503 +141,6 @@ pub struct StorageStatus { pub stick_initialized: bool, } -/// A Nitrokey device. -/// -/// This trait provides the commands that can be executed without authentication and that are -/// present on all supported Nitrokey devices. -pub trait Device<'a>: Authenticate<'a> + GetPasswordSafe<'a> + GenerateOtp + fmt::Debug { - /// Returns the [`Manager`][] instance that has been used to connect to this device. - /// - /// # Example - /// - /// ``` - /// use nitrokey::{Device, DeviceWrapper}; - /// - /// fn do_something(device: DeviceWrapper) { - /// // reconnect to any device - /// let manager = device.into_manager(); - /// let device = manager.connect(); - /// // do something with the device - /// // ... - /// } - /// - /// # fn main() -> Result<(), nitrokey::Error> { - /// match nitrokey::take()?.connect() { - /// Ok(device) => do_something(device), - /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), - /// } - /// # Ok(()) - /// # } - /// ``` - fn into_manager(self) -> &'a mut crate::Manager; - - /// Returns the model of the connected Nitrokey device. - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect()?; - /// println!("Connected to a Nitrokey {}", device.get_model()); - /// # Ok(()) - /// # } - fn get_model(&self) -> Model; - - /// Returns the serial number of the Nitrokey device. The serial number is the string - /// representation of a hex number. - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect()?; - /// match device.get_serial_number() { - /// Ok(number) => println!("serial no: {}", number), - /// Err(err) => eprintln!("Could not get serial number: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - fn get_serial_number(&self) -> Result<String, Error> { - result_from_string(unsafe { nitrokey_sys::NK_device_serial_number() }) - } - - /// Returns the number of remaining authentication attempts for the user. The total number of - /// available attempts is three. - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect()?; - /// let count = device.get_user_retry_count(); - /// match device.get_user_retry_count() { - /// Ok(count) => println!("{} remaining authentication attempts (user)", count), - /// Err(err) => eprintln!("Could not get user retry count: {}", err), - /// } - /// # Ok(()) - /// # } - /// ``` - fn get_user_retry_count(&self) -> Result<u8, Error> { - result_or_error(unsafe { nitrokey_sys::NK_get_user_retry_count() }) - } - - /// Returns the number of remaining authentication attempts for the admin. The total number of - /// available attempts is three. - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect()?; - /// let count = device.get_admin_retry_count(); - /// match device.get_admin_retry_count() { - /// Ok(count) => println!("{} remaining authentication attempts (admin)", count), - /// Err(err) => eprintln!("Could not get admin retry count: {}", err), - /// } - /// # Ok(()) - /// # } - /// ``` - fn get_admin_retry_count(&self) -> Result<u8, Error> { - result_or_error(unsafe { nitrokey_sys::NK_get_admin_retry_count() }) - } - - /// Returns the firmware version. - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect()?; - /// match device.get_firmware_version() { - /// Ok(version) => println!("Firmware version: {}", version), - /// Err(err) => eprintln!("Could not access firmware version: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - fn get_firmware_version(&self) -> Result<FirmwareVersion, Error> { - let major = result_or_error(unsafe { nitrokey_sys::NK_get_major_firmware_version() })?; - let minor = result_or_error(unsafe { nitrokey_sys::NK_get_minor_firmware_version() })?; - Ok(FirmwareVersion { major, minor }) - } - - /// Returns the current configuration of the Nitrokey device. - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let device = manager.connect()?; - /// let config = device.get_config()?; - /// println!("numlock binding: {:?}", config.numlock); - /// println!("capslock binding: {:?}", config.capslock); - /// println!("scrollock binding: {:?}", config.scrollock); - /// println!("require password for OTP: {:?}", config.user_password); - /// # Ok(()) - /// # } - /// ``` - fn get_config(&self) -> Result<Config, Error> { - let config_ptr = unsafe { nitrokey_sys::NK_read_config() }; - if config_ptr.is_null() { - return Err(get_last_error()); - } - let config_array_ptr = config_ptr as *const [u8; 5]; - let raw_config = unsafe { RawConfig::from(*config_array_ptr) }; - unsafe { libc::free(config_ptr as *mut libc::c_void) }; - Ok(raw_config.into()) - } - - /// Changes the administrator PIN. - /// - /// # Errors - /// - /// - [`InvalidString`][] if one of the provided passwords contains a null byte - /// - [`WrongPassword`][] if the current admin password is wrong - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect()?; - /// match device.change_admin_pin("12345678", "12345679") { - /// Ok(()) => println!("Updated admin PIN."), - /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_admin_pin(&mut self, current: &str, new: &str) -> Result<(), Error> { - let current_string = get_cstring(current)?; - let new_string = get_cstring(new)?; - get_command_result(unsafe { - nitrokey_sys::NK_change_admin_PIN(current_string.as_ptr(), new_string.as_ptr()) - }) - } - - /// Changes the user PIN. - /// - /// # Errors - /// - /// - [`InvalidString`][] if one of the provided passwords contains a null byte - /// - [`WrongPassword`][] if the current user password is wrong - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect()?; - /// match device.change_user_pin("123456", "123457") { - /// Ok(()) => println!("Updated admin PIN."), - /// Err(err) => eprintln!("Failed to update admin PIN: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn change_user_pin(&mut self, current: &str, new: &str) -> Result<(), Error> { - let current_string = get_cstring(current)?; - let new_string = get_cstring(new)?; - get_command_result(unsafe { - nitrokey_sys::NK_change_user_PIN(current_string.as_ptr(), new_string.as_ptr()) - }) - } - - /// Unlocks the user PIN after three failed login attempts and sets it to the given value. - /// - /// # Errors - /// - /// - [`InvalidString`][] if one of the provided passwords contains a null byte - /// - [`WrongPassword`][] if the admin password is wrong - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect()?; - /// match device.unlock_user_pin("12345678", "123456") { - /// Ok(()) => println!("Unlocked user PIN."), - /// Err(err) => eprintln!("Failed to unlock user PIN: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`InvalidString`]: enum.LibraryError.html#variant.InvalidString - /// [`WrongPassword`]: enum.CommandError.html#variant.WrongPassword - fn unlock_user_pin(&mut self, admin_pin: &str, user_pin: &str) -> Result<(), Error> { - let admin_pin_string = get_cstring(admin_pin)?; - let user_pin_string = get_cstring(user_pin)?; - get_command_result(unsafe { - nitrokey_sys::NK_unlock_user_password( - admin_pin_string.as_ptr(), - user_pin_string.as_ptr(), - ) - }) - } - - /// Locks the Nitrokey device. - /// - /// This disables the password store if it has been unlocked. On the Nitrokey Storage, this - /// also disables the volumes if they have been enabled. - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect()?; - /// match device.lock() { - /// Ok(()) => println!("Locked the Nitrokey device."), - /// Err(err) => eprintln!("Could not lock the Nitrokey device: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - fn lock(&mut self) -> Result<(), Error> { - get_command_result(unsafe { nitrokey_sys::NK_lock_device() }) - } - - /// Performs a factory reset on the Nitrokey device. - /// - /// This commands performs a factory reset on the smart card (like the factory reset via `gpg - /// --card-edit`) and then clears the flash memory (password safe, one-time passwords etc.). - /// After a factory reset, [`build_aes_key`][] has to be called before the password safe or the - /// encrypted volume can be used. - /// - /// # Errors - /// - /// - [`InvalidString`][] if the provided password contains a null byte - /// - [`WrongPassword`][] if the admin password is wrong - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect()?; - /// match device.factory_reset("12345678") { - /// Ok(()) => println!("Performed a factory reset."), - /// Err(err) => eprintln!("Could not perform a factory reset: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`build_aes_key`]: #method.build_aes_key - fn factory_reset(&mut self, admin_pin: &str) -> Result<(), Error> { - let admin_pin_string = get_cstring(admin_pin)?; - get_command_result(unsafe { nitrokey_sys::NK_factory_reset(admin_pin_string.as_ptr()) }) - } - - /// Builds a new AES key on the Nitrokey. - /// - /// The AES key is used to encrypt the password safe and the encrypted volume. You may need - /// to call this method after a factory reset, either using [`factory_reset`][] or using `gpg - /// --card-edit`. You can also use it to destroy the data stored in the password safe or on - /// the encrypted volume. - /// - /// # Errors - /// - /// - [`InvalidString`][] if the provided password contains a null byte - /// - [`WrongPassword`][] if the admin password is wrong - /// - /// # Example - /// - /// ```no_run - /// use nitrokey::Device; - /// # use nitrokey::Error; - /// - /// # fn try_main() -> Result<(), Error> { - /// let mut manager = nitrokey::take()?; - /// let mut device = manager.connect()?; - /// match device.build_aes_key("12345678") { - /// Ok(()) => println!("New AES keys have been built."), - /// Err(err) => eprintln!("Could not build new AES keys: {}", err), - /// }; - /// # Ok(()) - /// # } - /// ``` - /// - /// [`factory_reset`]: #method.factory_reset - fn build_aes_key(&mut self, admin_pin: &str) -> Result<(), Error> { - let admin_pin_string = get_cstring(admin_pin)?; - get_command_result(unsafe { nitrokey_sys::NK_build_aes_key(admin_pin_string.as_ptr()) }) - } -} - -fn get_connected_model() -> Option<Model> { - match unsafe { nitrokey_sys::NK_get_device_model() } { - nitrokey_sys::NK_device_model_NK_PRO => Some(Model::Pro), - nitrokey_sys::NK_device_model_NK_STORAGE => Some(Model::Storage), - _ => None, - } -} - -pub(crate) fn create_device_wrapper( - manager: &mut crate::Manager, - model: Model, -) -> DeviceWrapper<'_> { - match model { - Model::Pro => Pro::new(manager).into(), - Model::Storage => Storage::new(manager).into(), - } -} - -pub(crate) fn get_connected_device( - manager: &mut crate::Manager, -) -> Result<DeviceWrapper<'_>, Error> { - match get_connected_model() { - Some(model) => Ok(create_device_wrapper(manager, model)), - None => Err(CommunicationError::NotConnected.into()), - } -} - -pub(crate) fn connect_enum(model: Model) -> bool { - let model = match model { - Model::Storage => nitrokey_sys::NK_device_model_NK_STORAGE, - Model::Pro => nitrokey_sys::NK_device_model_NK_PRO, - }; - unsafe { nitrokey_sys::NK_login_enum(model) == 1 } -} - -impl<'a> DeviceWrapper<'a> { - fn device(&self) -> &dyn Device<'a> { - match *self { - DeviceWrapper::Storage(ref storage) => storage, - DeviceWrapper::Pro(ref pro) => pro, - } - } - - fn device_mut(&mut self) -> &mut dyn Device<'a> { - match *self { - DeviceWrapper::Storage(ref mut storage) => storage, - DeviceWrapper::Pro(ref mut pro) => pro, - } - } -} - -impl<'a> From<Pro<'a>> for DeviceWrapper<'a> { - fn from(device: Pro<'a>) -> Self { - DeviceWrapper::Pro(device) - } -} - -impl<'a> From<Storage<'a>> for DeviceWrapper<'a> { - fn from(device: Storage<'a>) -> Self { - DeviceWrapper::Storage(device) - } -} - -impl<'a> GenerateOtp for DeviceWrapper<'a> { - fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> { - self.device().get_hotp_slot_name(slot) - } - - fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> { - self.device().get_totp_slot_name(slot) - } - - fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> { - self.device_mut().get_hotp_code(slot) - } - - fn get_totp_code(&self, slot: u8) -> Result<String, Error> { - self.device().get_totp_code(slot) - } -} - -impl<'a> Device<'a> for DeviceWrapper<'a> { - fn into_manager(self) -> &'a mut crate::Manager { - match self { - DeviceWrapper::Pro(dev) => dev.into_manager(), - DeviceWrapper::Storage(dev) => dev.into_manager(), - } - } - - fn get_model(&self) -> Model { - match *self { - DeviceWrapper::Pro(_) => Model::Pro, - DeviceWrapper::Storage(_) => Model::Storage, - } - } -} - -impl<'a> Pro<'a> { - pub(crate) fn new(manager: &'a mut crate::Manager) -> Pro<'a> { - Pro { - manager: Some(manager), - } - } -} - -impl<'a> Drop for Pro<'a> { - fn drop(&mut self) { - unsafe { - nitrokey_sys::NK_logout(); - } - } -} - -impl<'a> Device<'a> for Pro<'a> { - fn into_manager(mut self) -> &'a mut crate::Manager { - self.manager.take().unwrap() - } - - fn get_model(&self) -> Model { - Model::Pro - } -} - -impl<'a> GenerateOtp for Pro<'a> {} - impl<'a> Storage<'a> { pub(crate) fn new(manager: &'a mut crate::Manager) -> Storage<'a> { Storage { diff --git a/nitrokey/src/device/wrapper.rs b/nitrokey/src/device/wrapper.rs new file mode 100644 index 0000000..a3a18f9 --- /dev/null +++ b/nitrokey/src/device/wrapper.rs @@ -0,0 +1,134 @@ +// Copyright (C) 2018-2019 Robin Krahl <robin.krahl@ireas.org> +// SPDX-License-Identifier: MIT + +use crate::device::{Device, Model, Pro, Storage}; +use crate::error::Error; +use crate::otp::GenerateOtp; + +/// A wrapper for a Nitrokey device of unknown type. +/// +/// Use the [`connect`][] method to obtain a wrapped instance. The wrapper implements all traits +/// that are shared between all Nitrokey devices so that the shared functionality can be used +/// without knowing the type of the underlying device. If you want to use functionality that is +/// not available for all devices, you have to extract the device. +/// +/// # Examples +/// +/// Authentication with error handling: +/// +/// ```no_run +/// use nitrokey::{Authenticate, DeviceWrapper, User}; +/// # use nitrokey::Error; +/// +/// fn perform_user_task<'a>(device: &User<'a, DeviceWrapper<'a>>) {} +/// fn perform_other_task(device: &DeviceWrapper) {} +/// +/// # fn try_main() -> Result<(), Error> { +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect()?; +/// let device = match device.authenticate_user("123456") { +/// Ok(user) => { +/// perform_user_task(&user); +/// user.device() +/// }, +/// Err((device, err)) => { +/// eprintln!("Could not authenticate as user: {}", err); +/// device +/// }, +/// }; +/// perform_other_task(&device); +/// # Ok(()) +/// # } +/// ``` +/// +/// Device-specific commands: +/// +/// ```no_run +/// use nitrokey::{DeviceWrapper, Storage}; +/// # use nitrokey::Error; +/// +/// fn perform_common_task(device: &DeviceWrapper) {} +/// fn perform_storage_task(device: &Storage) {} +/// +/// # fn try_main() -> Result<(), Error> { +/// let mut manager = nitrokey::take()?; +/// let device = manager.connect()?; +/// perform_common_task(&device); +/// match device { +/// DeviceWrapper::Storage(storage) => perform_storage_task(&storage), +/// _ => (), +/// }; +/// # Ok(()) +/// # } +/// ``` +/// +/// [`connect`]: struct.Manager.html#method.connect +#[derive(Debug)] +pub enum DeviceWrapper<'a> { + /// A Nitrokey Storage device. + Storage(Storage<'a>), + /// A Nitrokey Pro device. + Pro(Pro<'a>), +} + +impl<'a> DeviceWrapper<'a> { + fn device(&self) -> &dyn Device<'a> { + match *self { + DeviceWrapper::Storage(ref storage) => storage, + DeviceWrapper::Pro(ref pro) => pro, + } + } + + fn device_mut(&mut self) -> &mut dyn Device<'a> { + match *self { + DeviceWrapper::Storage(ref mut storage) => storage, + DeviceWrapper::Pro(ref mut pro) => pro, + } + } +} + +impl<'a> From<Pro<'a>> for DeviceWrapper<'a> { + fn from(device: Pro<'a>) -> Self { + DeviceWrapper::Pro(device) + } +} + +impl<'a> From<Storage<'a>> for DeviceWrapper<'a> { + fn from(device: Storage<'a>) -> Self { + DeviceWrapper::Storage(device) + } +} + +impl<'a> GenerateOtp for DeviceWrapper<'a> { + fn get_hotp_slot_name(&self, slot: u8) -> Result<String, Error> { + self.device().get_hotp_slot_name(slot) + } + + fn get_totp_slot_name(&self, slot: u8) -> Result<String, Error> { + self.device().get_totp_slot_name(slot) + } + + fn get_hotp_code(&mut self, slot: u8) -> Result<String, Error> { + self.device_mut().get_hotp_code(slot) + } + + fn get_totp_code(&self, slot: u8) -> Result<String, Error> { + self.device().get_totp_code(slot) + } +} + +impl<'a> Device<'a> for DeviceWrapper<'a> { + fn into_manager(self) -> &'a mut crate::Manager { + match self { + DeviceWrapper::Pro(dev) => dev.into_manager(), + DeviceWrapper::Storage(dev) => dev.into_manager(), + } + } + + fn get_model(&self) -> Model { + match *self { + DeviceWrapper::Pro(_) => Model::Pro, + DeviceWrapper::Storage(_) => Model::Storage, + } + } +} diff --git a/nitrokey/src/lib.rs b/nitrokey/src/lib.rs index a4402c5..059792d 100644 --- a/nitrokey/src/lib.rs +++ b/nitrokey/src/lib.rs @@ -243,14 +243,12 @@ impl Manager { /// /// fn do_something(device: DeviceWrapper) {} /// - /// # fn main() -> Result<(), nitrokey::Error> { /// let mut manager = nitrokey::take()?; /// match manager.connect() { /// Ok(device) => do_something(device), /// Err(err) => println!("Could not connect to a Nitrokey: {}", err), /// } - /// # Ok(()) - /// # } + /// # Ok::<(), nitrokey::Error>(()) /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -276,13 +274,11 @@ impl Manager { /// /// fn do_something(device: DeviceWrapper) {} /// - /// # fn main() -> Result<(), nitrokey::Error> { /// match nitrokey::take()?.connect_model(Model::Pro) { /// Ok(device) => do_something(device), /// Err(err) => println!("Could not connect to a Nitrokey Pro: {}", err), /// } - /// # Ok(()) - /// # } + /// # Ok::<(), nitrokey::Error>(()) /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -307,13 +303,11 @@ impl Manager { /// /// fn use_pro(device: Pro) {} /// - /// # fn main() -> Result<(), nitrokey::Error> { /// match nitrokey::take()?.connect_pro() { /// Ok(device) => use_pro(device), /// Err(err) => println!("Could not connect to the Nitrokey Pro: {}", err), /// } - /// # Ok(()) - /// # } + /// # Ok::<(), nitrokey::Error>(()) /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -338,13 +332,11 @@ impl Manager { /// /// fn use_storage(device: Storage) {} /// - /// # fn main() -> Result<(), nitrokey::Error> { /// match nitrokey::take()?.connect_storage() { /// Ok(device) => use_storage(device), /// Err(err) => println!("Could not connect to the Nitrokey Storage: {}", err), /// } - /// # Ok(()) - /// # } + /// # Ok::<(), nitrokey::Error>(()) /// ``` /// /// [`NotConnected`]: enum.CommunicationError.html#variant.NotConnected @@ -453,11 +445,9 @@ pub fn set_log_level(level: LogLevel) { /// # Example /// /// ``` -/// # fn main() -> Result<(), nitrokey::Error> { /// let version = nitrokey::get_library_version()?; /// println!("Using libnitrokey {}", version.git); -/// # Ok(()) -/// # } +/// # Ok::<(), nitrokey::Error>(()) /// ``` /// /// [`Utf8Error`]: enum.Error.html#variant.Utf8Error diff --git a/nitrokey/src/util.rs b/nitrokey/src/util.rs index fdb73c3..5a56c55 100644 --- a/nitrokey/src/util.rs +++ b/nitrokey/src/util.rs @@ -5,8 +5,7 @@ use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int}; use libc::{c_void, free}; -use rand_core::RngCore; -use rand_os::OsRng; +use rand_core::{OsRng, RngCore}; use crate::error::{Error, LibraryError}; @@ -77,9 +76,8 @@ pub fn get_last_error() -> Error { } pub fn generate_password(length: usize) -> Result<Vec<u8>, Error> { - let mut rng = OsRng::new().map_err(|err| Error::RandError(Box::new(err)))?; let mut data = vec![0u8; length]; - rng.fill_bytes(&mut data[..]); + OsRng.fill_bytes(&mut data[..]); Ok(data) } diff --git a/nitrokey/tests/otp.rs b/nitrokey/tests/otp.rs index aafda59..38cd8a9 100644 --- a/nitrokey/tests/otp.rs +++ b/nitrokey/tests/otp.rs @@ -44,12 +44,12 @@ where unwrap_ok!(device.authenticate_admin(DEFAULT_ADMIN_PIN)) } -fn configure_hotp(admin: &mut ConfigureOtp, counter: u8) { +fn configure_hotp(admin: &mut dyn ConfigureOtp, counter: u8) { let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); assert_ok!((), admin.write_hotp_slot(slot_data, counter.into())); } -fn check_hotp_codes(device: &mut GenerateOtp, offset: u8) { +fn check_hotp_codes(device: &mut dyn GenerateOtp, offset: u8) { HOTP_CODES.iter().enumerate().for_each(|(i, code)| { if i >= offset as usize { assert_ok!(code.to_string(), device.get_hotp_code(1)); @@ -146,13 +146,13 @@ fn hotp_erase(device: DeviceWrapper) { assert_ok!("test2".to_string(), device.get_hotp_slot_name(2)); } -fn configure_totp(admin: &mut ConfigureOtp, factor: u64) { +fn configure_totp(admin: &mut dyn ConfigureOtp, factor: u64) { let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); let time_window = 30u64.checked_mul(factor).unwrap(); assert_ok!((), admin.write_totp_slot(slot_data, time_window as u16)); } -fn check_totp_codes(device: &mut GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { +fn check_totp_codes(device: &mut dyn GenerateOtp, factor: u64, timestamp_size: TotpTimestampSize) { for (base_time, codes) in TOTP_CODES { let time = base_time.checked_mul(factor).unwrap(); let is_u64 = time > u32::max_value() as u64; diff --git a/rand/.github/ISSUE_TEMPLATE/compile-issue.md b/rand/.github/ISSUE_TEMPLATE/compile-issue.md new file mode 100644 index 0000000..8a8354b --- /dev/null +++ b/rand/.github/ISSUE_TEMPLATE/compile-issue.md @@ -0,0 +1,16 @@ +--- +name: Compile issue +about: Report / ask about a compilation issue +title: '' +labels: '' +assignees: '' + +--- + +# Common issues + +**Problem**: `rand_hc::Hc128Rng: rand_core::SeedableRng` (or other RNG) + +**Quick solution**: `cargo update` + +**Details**: This happens when multiple versions of the `rand_core` crate are in use. Check your `Cargo.lock` file for all versions of `rand_core`. Note that some versions (0.2.2 and 0.3.1) are compatibility shims and are not a problem by themselves. diff --git a/rand/.github/ISSUE_TEMPLATE/feature_request.md b/rand/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..90c57c8 --- /dev/null +++ b/rand/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,18 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +## Background + +**What is your motivation?** + +**What type of application is this?** (E.g. cryptography, game, numerical simulation) + +## Feature request + +<details here> diff --git a/rand/.github/ISSUE_TEMPLATE/other.md b/rand/.github/ISSUE_TEMPLATE/other.md new file mode 100644 index 0000000..a3e76ca --- /dev/null +++ b/rand/.github/ISSUE_TEMPLATE/other.md @@ -0,0 +1,10 @@ +--- +name: Other +about: empty template +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/rand/.gitignore b/rand/.gitignore index a9d37c5..ac38e81 100644 --- a/rand/.gitignore +++ b/rand/.gitignore @@ -1,2 +1,4 @@ target Cargo.lock +rand_wasm_bindgen_test*.[tj]s +rand_wasm_bindgen_test*.wasm diff --git a/rand/.travis.yml b/rand/.travis.yml index b41e681..f3790dc 100644 --- a/rand/.travis.yml +++ b/rand/.travis.yml @@ -34,11 +34,11 @@ sudo: false # CRATE FEATURES, TESTS, AND SUB-CRATES # Goal: Run unit tests, doctests, examples, and test benchmarks for all crates, # in configurations that cover all interesting combinations of features. -# (`--lib` only runs unit tests just like `--tests`, but the latter is not -# available in Rust 1.22.0) # Tests run on rand: # - test no_std support, but only the unit tests: -# `cargo test --lib --no-default-features` +# `cargo test --tests --no-default-features` +# - test no_std support, including the alloc feature: +# cargo test --tests --no-default-features --features=alloc # - run unit tests and doctests with all features which are available on stable: # `cargo test --features=serde1,log` # - test examples: @@ -46,8 +46,6 @@ sudo: false # Additional tests on nightly: # - run unit tests and doctests with all features which are available on nightly: # `cargo test --all-features` -# - test no_std support, including the nightly alloc feature: -# cargo test --lib --no-default-features --features=alloc # - run benchmarks as tests: # `cargo test --benches --features=nightly` # Tests on subcrates: @@ -56,72 +54,106 @@ sudo: false # # TODO: SIMD support on stable releases # NOTE: SIMD support is unreliable on nightly; we track the latest release +# NOTE: Test for alloc feature in no_std is not included here because it depends +# on the alloc crate stabilized in Rust 1.36. matrix: include: - - rust: 1.22.0 - env: DESCRIPTION="pinned stable Rust release" + - rust: 1.32.0 + env: DESCRIPTION="Linux, 1.32.0" + os: linux script: # Differs from standard script: rand_pcg features - - cargo test --lib --no-default-features + - cargo test --tests --no-default-features # TODO: add simd_support feature: - cargo test --features=serde1,log - cargo test --examples - cargo test --manifest-path rand_core/Cargo.toml - cargo test --manifest-path rand_core/Cargo.toml --no-default-features + - cargo test --manifest-path rand_distr/Cargo.toml - cargo test --manifest-path rand_isaac/Cargo.toml --features=serde1 # TODO: cannot test rand_pcg due to explicit dependency on i128 - cargo test --manifest-path rand_xorshift/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xoshiro/Cargo.toml - cargo test --manifest-path rand_chacha/Cargo.toml - cargo test --manifest-path rand_hc/Cargo.toml + - cargo test --manifest-path rand_jitter/Cargo.toml - cargo test --manifest-path rand_os/Cargo.toml + - rust: 1.32.0 + env: DESCRIPTION="OSX, 1.32.0" + os: osx + script: + # Differs from standard script: rand_pcg features + - cargo test --tests --no-default-features + # TODO: add simd_support feature: + - cargo test --features=serde1,log + - cargo test --examples + - cargo test --manifest-path rand_core/Cargo.toml + - cargo test --manifest-path rand_core/Cargo.toml --no-default-features + - cargo test --manifest-path rand_distr/Cargo.toml + - cargo test --manifest-path rand_isaac/Cargo.toml --features=serde1 + # TODO: cannot test rand_pcg due to explicit dependency on i128 + - cargo test --manifest-path rand_xorshift/Cargo.toml --features=serde1 + - cargo test --manifest-path rand_xoshiro/Cargo.toml + - cargo test --manifest-path rand_chacha/Cargo.toml + - cargo test --manifest-path rand_hc/Cargo.toml + - cargo test --manifest-path rand_jitter/Cargo.toml + - cargo test --manifest-path rand_os/Cargo.toml + + - rust: stable + env: DESCRIPTION="Linux, stable" + - rust: stable - env: DESCRIPTION="stable Rust release, macOS, iOS (cross-compile only)" + env: DESCRIPTION="OSX+iOS, stable" os: osx install: - rustup target add aarch64-apple-ios script: # Differs from standard script: includes aarch64-apple-ios cross-build - - cargo test --lib --no-default-features + - cargo test --tests --no-default-features # TODO: add simd_support feature: - cargo test --features=serde1,log - cargo test --examples - cargo test --manifest-path rand_core/Cargo.toml - cargo test --manifest-path rand_core/Cargo.toml --no-default-features + - cargo test --manifest-path rand_distr/Cargo.toml - cargo test --manifest-path rand_isaac/Cargo.toml --features=serde1 - cargo test --manifest-path rand_pcg/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xorshift/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xoshiro/Cargo.toml - cargo test --manifest-path rand_chacha/Cargo.toml - cargo test --manifest-path rand_hc/Cargo.toml + - cargo test --manifest-path rand_jitter/Cargo.toml - cargo test --manifest-path rand_os/Cargo.toml - cargo build --target=aarch64-apple-ios - rust: beta - env: DESCRIPTION="beta Rust release" + env: DESCRIPTION="Linux, beta" - rust: nightly - env: DESCRIPTION="nightly features, benchmarks, documentation" + os: linux + env: DESCRIPTION="Linux, nightly, docs" install: - cargo --list | egrep "^\s*deadlinks$" -q || cargo install cargo-deadlinks - cargo deadlinks -V before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - # Differs from standard script: alloc feature, all features, doc build - - cargo test --lib --no-default-features --features=alloc + # Differs from standard script: all features, doc build + - cargo test --tests --no-default-features --features=alloc - cargo test --all-features - cargo test --benches --features=nightly - cargo test --examples - cargo test --manifest-path rand_core/Cargo.toml - cargo test --manifest-path rand_core/Cargo.toml --no-default-features --features=alloc + - cargo test --manifest-path rand_distr/Cargo.toml - cargo test --manifest-path rand_isaac/Cargo.toml --features=serde1 - cargo test --manifest-path rand_pcg/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xorshift/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xoshiro/Cargo.toml - cargo test --manifest-path rand_chacha/Cargo.toml - cargo test --manifest-path rand_hc/Cargo.toml + - cargo test --manifest-path rand_jitter/Cargo.toml - cargo test --manifest-path rand_os/Cargo.toml # remove cached documentation, otherwise files from previous PRs can get included - rm -rf target/doc @@ -131,6 +163,34 @@ matrix: - travis-cargo --only nightly doc-upload - rust: nightly + os: osx + env: DESCRIPTION="OSX, nightly, docs" + install: + - cargo --list | egrep "^\s*deadlinks$" -q || cargo install cargo-deadlinks + - cargo deadlinks -V + script: + # Differs from standard script: all features, doc build + - cargo test --tests --no-default-features --features=alloc + - cargo test --all-features + - cargo test --benches --features=nightly + - cargo test --examples + - cargo test --manifest-path rand_core/Cargo.toml + - cargo test --manifest-path rand_core/Cargo.toml --no-default-features --features=alloc + - cargo test --manifest-path rand_distr/Cargo.toml + - cargo test --manifest-path rand_isaac/Cargo.toml --features=serde1 + - cargo test --manifest-path rand_pcg/Cargo.toml --features=serde1 + - cargo test --manifest-path rand_xorshift/Cargo.toml --features=serde1 + - cargo test --manifest-path rand_xoshiro/Cargo.toml + - cargo test --manifest-path rand_chacha/Cargo.toml + - cargo test --manifest-path rand_hc/Cargo.toml + - cargo test --manifest-path rand_jitter/Cargo.toml + - cargo test --manifest-path rand_os/Cargo.toml + # remove cached documentation, otherwise files from previous PRs can get included + - rm -rf target/doc + - cargo doc --no-deps --all --all-features + - cargo deadlinks --dir target/doc + + - rust: nightly env: DESCRIPTION="WASM via emscripten, stdweb and wasm-bindgen" install: - rustup target add wasm32-unknown-unknown @@ -139,6 +199,8 @@ matrix: - ./utils/ci/install_cargo_web.sh - cargo web prepare-emscripten - cargo web -V + - cargo list | grep install-update || cargo install -f cargo-update + - cargo install-update -i cargo-update wasm-bindgen-cli wasm-pack addons: chrome: stable script: @@ -151,6 +213,10 @@ matrix: #- cargo build --target wasm32-unknown-unknown # without any features - cargo build --target wasm32-unknown-unknown --features=wasm-bindgen - cargo web test --target wasm32-unknown-unknown --features=stdweb + - cargo build --manifest-path tests/wasm_bindgen/Cargo.toml --target wasm32-unknown-unknown + - wasm-bindgen --nodejs target/wasm32-unknown-unknown/debug/rand_wasm_bindgen_test.wasm --out-dir tests/wasm_bindgen/js + - node tests/wasm_bindgen/js/index.js + - wasm-pack test --node tests/wasm_bindgen - rust: nightly env: DESCRIPTION="cross-platform builder (doesn't run tests)" @@ -162,6 +228,7 @@ matrix: - rustup target add x86_64-unknown-netbsd - rustup target add x86_64-unknown-redox script: + # Test the top-level crate with all features: - cargo build --target=x86_64-sun-solaris --all-features - cargo build --target=x86_64-unknown-cloudabi --all-features - cargo build --target=x86_64-unknown-freebsd --all-features @@ -190,24 +257,42 @@ matrix: - source ~/.cargo/env || true script: - bash utils/ci/script.sh + - rust: nightly + env: DESCRIPTION="no_std platform test" + install: + - rustup target add thumbv6m-none-eabi + script: + # Test the top-level crate with all features: + - cargo build --target=thumbv6m-none-eabi --no-default-features + + - rust: nightly + os: linux + env: DESCRIPTION="Miri, nightly" + script: + - sh utils/ci/miri.sh before_install: - set -e - rustup self update script: - - cargo test --lib --no-default-features + - cargo test --tests --no-default-features + - cargo test --tests --no-default-features --features getrandom + - cargo test --tests --no-default-features --features=alloc # TODO: add simd_support feature: - cargo test --features=serde1,log - cargo test --examples - cargo test --manifest-path rand_core/Cargo.toml - cargo test --manifest-path rand_core/Cargo.toml --no-default-features + - cargo test --manifest-path rand_core/Cargo.toml --no-default-features --features=alloc + - cargo test --manifest-path rand_distr/Cargo.toml - cargo test --manifest-path rand_isaac/Cargo.toml --features=serde1 - cargo test --manifest-path rand_pcg/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xorshift/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xoshiro/Cargo.toml - cargo test --manifest-path rand_chacha/Cargo.toml - cargo test --manifest-path rand_hc/Cargo.toml + - cargo test --manifest-path rand_jitter/Cargo.toml - cargo test --manifest-path rand_os/Cargo.toml after_script: set +e diff --git a/rand/CHANGELOG.md b/rand/CHANGELOG.md index 6aa0a24..a2ae496 100644 --- a/rand/CHANGELOG.md +++ b/rand/CHANGELOG.md @@ -8,6 +8,65 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md). You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful. +## [Unreleased] +- Fix `no_std` behaviour, appropriately enable c2-chacha's `std` feature (#844) +- Add a `no_std` target to CI to continously evaluate `no_std` status (#844) +- `alloc` feature in `no_std` is available since Rust 1.36 (#856) + +## [0.7.0] - 2019-06-28 + +### Fixes +- Fix incorrect pointer usages revealed by Miri testing (#780, #781) +- Fix (tiny!) bias in `Uniform` for 8- and 16-bit ints (#809) + +### Crate +- Bumped MSRV (min supported Rust version) to 1.32.0 +- Updated to Rust Edition 2018 (#823, #824) +- Removed dependence on `rand_xorshift`, `rand_isaac`, `rand_jitter` crates (#759, #765) +- Remove dependency on `winapi` (#724) +- Removed all `build.rs` files (#824) +- Removed code already deprecated in version 0.6 (#757) +- Removed the serde1 feature (It's still available for backwards compatibility, but it does not do anything. #830) +- Many documentation changes + +### rand_core +- Updated to `rand_core` 0.5.0 +- `Error` type redesigned with new API (#800) +- Move `from_entropy` method to `SeedableRng` and remove `FromEntropy` (#800) +- `SeedableRng::from_rng` is now expected to be value-stable (#815) + +### Standard RNGs +- OS interface moved from `rand_os` to new `getrandom` crate (#765, [getrandom](https://github.com/rust-random/getrandom)) +- Use ChaCha for `StdRng` and `ThreadRng` (#792) +- Feature-gate `SmallRng` (#792) +- `ThreadRng` now supports `Copy` (#758) +- Deprecated `EntropyRng` (#765) +- Enable fork protection of ReseedingRng without `std` (#724) + +### Distributions +- Many distributions have been moved to `rand_distr` (#761) +- `Bernoulli::new` constructor now returns a `Result` (#803) +- `Distribution::sample_iter` adjusted for more flexibility (#758) +- Added `distributions::weighted::alias_method::WeightedIndex` for `O(1)` sampling (#692) +- Support sampling `NonZeroU*` types with the `Standard` distribution (#728) +- Optimised `Binomial` distribution sampling (#735, #740, #752) +- Optimised SIMD float sampling (#739) + +### Sequences +- Make results portable across 32- and 64-bit by using `u32` samples for `usize` where possible (#809) + +## [0.6.5] - 2019-01-28 +### Crates +- Update `rand_core` to 0.4 (#703) +- Move `JitterRng` to its own crate (#685) +- Add a wasm-bindgen test crate (#696) + +### Platforms +- Fuchsia: Replaced fuchsia-zircon with fuchsia-cprng + +### Doc +- Use RFC 1946 for doc links (#691) +- Fix some doc links and notes (#711) ## [0.6.4] - 2019-01-08 ### Fixes diff --git a/rand/Cargo.toml b/rand/Cargo.toml index d802d36..ef344a6 100644 --- a/rand/Cargo.toml +++ b/rand/Cargo.toml @@ -1,39 +1,48 @@ [package] name = "rand" -version = "0.6.4" +version = "0.7.0" authors = ["The Rand Project Developers", "The Rust Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" -documentation = "https://rust-random.github.io/rand" +documentation = "https://rust-random.github.io/rand/" homepage = "https://crates.io/crates/rand" description = """ Random number generators and other randomness functionality. """ keywords = ["random", "rng"] categories = ["algorithms", "no-std"] -build = "build.rs" exclude = ["/utils/*", "/.travis.yml", "/appveyor.yml", ".gitignore"] +autobenches = true +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } [features] -default = ["std", "rand_os"] # without "std" rand uses libcore +# Meta-features: +default = ["std"] # without "std" rand uses libcore nightly = ["simd_support"] # enables all features requiring nightly rust -std = ["rand_core/std", "alloc", "rand_os"] +serde1 = [] # does nothing, deprecated + +# Optional dependencies: +std = ["rand_core/std", "rand_chacha/std", "alloc", "getrandom"] alloc = ["rand_core/alloc"] # enables Vec and Box support (without std) -i128_support = [] # enables i128 and u128 support -simd_support = ["packed_simd"] # enables SIMD support -serde1 = ["rand_core/serde1", "rand_isaac/serde1", "rand_xorshift/serde1"] # enables serialization for PRNGs # re-export optional WASM dependencies to avoid breakage: -wasm-bindgen = ["rand_os/wasm-bindgen"] -stdweb = ["rand_os/stdweb"] +wasm-bindgen = ["getrandom_package/wasm-bindgen"] +stdweb = ["getrandom_package/stdweb"] +getrandom = ["getrandom_package", "rand_core/getrandom"] + +# Configuration: +simd_support = ["packed_simd"] # enables SIMD support +small_rng = ["rand_pcg"] # enables SmallRng [workspace] members = [ "rand_core", + "rand_distr", + "rand_jitter", "rand_os", "rand_isaac", "rand_chacha", @@ -41,17 +50,14 @@ members = [ "rand_pcg", "rand_xorshift", "rand_xoshiro", + "tests/wasm_bindgen", ] [dependencies] -rand_core = { path = "rand_core", version = "0.3", default-features = false } -rand_pcg = { path = "rand_pcg", version = "0.1" } -rand_os = { path = "rand_os", version = "0.1", optional = true } -# only for deprecations and benches: -rand_isaac = { path = "rand_isaac", version = "0.1" } -rand_chacha = { path = "rand_chacha", version = "0.1" } -rand_hc = { path = "rand_hc", version = "0.1" } -rand_xorshift = { path = "rand_xorshift", version = "0.1" } +rand_core = { path = "rand_core", version = "0.5" } +rand_pcg = { path = "rand_pcg", version = "0.2", optional = true } +# Do not depend on 'getrandom_package' directly; use the 'getrandom' feature! +getrandom_package = { version = "0.1.1", package = "getrandom", optional = true } log = { version = "0.4", optional = true } [dependencies.packed_simd] @@ -62,23 +68,23 @@ optional = true features = ["into_bits"] [target.'cfg(unix)'.dependencies] -libc = { version = "0.2", default-features = false } +# Used for fork protection (reseeding.rs) +libc = { version = "0.2.22", default-features = false } -# TODO: check if all features are required -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "profileapi", "winnt"] } +# Emscripten does not support 128-bit integers, which are used by ChaCha code. +# We work around this by using a different RNG. +[target.'cfg(not(target_os = "emscripten"))'.dependencies] +rand_chacha = { path = "rand_chacha", version = "0.2.1", default-features = false } +[target.'cfg(target_os = "emscripten")'.dependencies] +rand_hc = { path = "rand_hc", version = "0.2" } [dev-dependencies] -# This has a histogram implementation used for testing uniformity. -average = "0.9.2" +rand_pcg = { path = "rand_pcg", version = "0.2" } # Only for benches: -rand_xoshiro = { path = "rand_xoshiro", version = "0.1" } - -[build-dependencies] -autocfg = "0.1" +rand_hc = { path = "rand_hc", version = "0.2" } +rand_xoshiro = { path = "rand_xoshiro", version = "0.3" } +rand_isaac = { path = "rand_isaac", version = "0.2" } +rand_xorshift = { path = "rand_xorshift", version = "0.2" } [package.metadata.docs.rs] all-features = true - -[patch.crates-io] -rand_core = { path = "rand_core", version = "0.3", default-features = false } diff --git a/rand/README.md b/rand/README.md index 314a57f..5acbadb 100644 --- a/rand/README.md +++ b/rand/README.md @@ -6,7 +6,7 @@ [![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand) [![API](https://docs.rs/rand/badge.svg)](https://docs.rs/rand) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) A Rust library for random number generation. @@ -30,7 +30,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -rand = "0.6" +rand = "0.7" ``` To get started using Rand, see [The Book](https://rust-random.github.io/book). @@ -38,10 +38,22 @@ To get started using Rand, see [The Book](https://rust-random.github.io/book). ## Versions +Rand libs have inter-dependencies and make use of the +[semver trick](https://github.com/dtolnay/semver-trick/) in order to make traits +compatible across crate versions. (This is especially important for `RngCore` +and `SeedableRng`.) A few crate releases are thus compatibility shims, +depending on the *next* lib version (e.g. `rand_core` versions `0.2.2` and +`0.3.1`). This means, for example, that `rand_core_0_4_0::SeedableRng` and +`rand_core_0_3_0::SeedableRng` are distinct, incompatible traits, which can +cause build errors. Usually, running `cargo update` is enough to fix any issues. + The Rand lib is not yet stable, however we are careful to limit breaking changes and warn via deprecation wherever possible. Patch versions never introduce breaking changes. The following minor versions are supported: +- Version 0.7 was released in June 2019, moving most non-uniform distributions + to an external crate, moving `from_entropy` to `SeedableRng`, and many small + changes and fixes. - Version 0.6 was released in November 2018, redesigning the `seq` module, moving most PRNGs to external crates, and many small changes. - Version 0.5 was released in May 2018, as a major reorganisation @@ -57,8 +69,9 @@ reading the [Upgrade Guide](https://rust-random.github.io/book/update.html). ### Rust version requirements -Since version 0.5, Rand requires **Rustc version 1.22 or greater**. -Rand 0.4 and 0.3 (since approx. June 2017) require Rustc version 1.15 or +Since version 0.7, Rand requires **Rustc version 1.32 or greater**. +Rand 0.5 requires Rustc 1.22 or greater while versions +0.4 and 0.3 (since approx. June 2017) require Rustc version 1.15 or greater. Subsets of the Rand code may work with older Rust versions, but this is not supported. @@ -66,52 +79,35 @@ Travis CI always has a build with a pinned version of Rustc matching the oldest supported Rust release. The current policy is that this can be updated in any Rand release if required, but the change must be noted in the changelog. -To avoid bumping the required version unnecessarily, we use a `build.rs` script -to auto-detect the compiler version and enable certain features or change code -paths automatically. Since this makes it easy to unintentionally make use of -features requiring a more recent Rust version, we recommend testing with a -pinned version of Rustc if you require compatibility with a specific version. - ## Crate Features -Rand is built with the `std` and `rand_os` features enabled by default: - -- `std` enables functionality dependent on the `std` lib and implies `alloc` - and `rand_os` -- `rand_os` enables the `rand_os` crate, `rngs::OsRng` and enables its usage; - the continued existance of this feature is not guaranteed so users are - encouraged to specify `std` instead - -The following optional features are available: - -- `alloc` can be used instead of `std` to provide `Vec` and `Box`. -- `log` enables some logging via the `log` crate. -- `nightly` enables all unstable features (`simd_support`). -- `serde1` enables serialization for some types, via Serde version 1. -- `simd_support` enables uniform sampling of SIMD types (integers and floats). -- `stdweb` enables support for `OsRng` on `wasm32-unknown-unknown` via `stdweb` - combined with `cargo-web`. -- `wasm-bindgen` enables support for `OsRng` on `wasm32-unknown-unknown` via - [`wasm-bindgen`] - -[`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen - -`no_std` mode is activated by setting `default-features = false`; this removes -functionality depending on `std`: - -- `thread_rng()`, and `random()` are not available, as they require thread-local - storage and an entropy source. -- `OsRng` and `EntropyRng` are unavailable. -- `JitterRng` code is still present, but a nanosecond timer must be provided via - `JitterRng::new_with_timer` -- Since no external entropy is available, it is not possible to create - generators with fresh seeds using the `FromEntropy` trait (user must provide - a seed). -- Several non-linear distributions distributions are unavailable since `exp` - and `log` functions are not provided in `core`. -- Large parts of the `seq`-uence module are unavailable, unless the `alloc` - feature is used (several APIs and many implementations require `Vec`). +Rand is built with these features enabled by default: + +- `std` enables functionality dependent on the `std` lib +- `alloc` (implied by `std`) enables functionality requiring an allocator (when using this feature in `no_std`, Rand requires Rustc version 1.36 or greater) +- `getrandom` (implied by `std`) is an optional dependency providing the code + behind `rngs::OsRng` + +Optionally, the following dependencies can be enabled: + +- `log` enables logging via the `log` crate +- `stdweb` implies `getrandom/stdweb` to enable + `getrandom` support on `wasm32-unknown-unknown` +- `wasm-bindgen` implies `getrandom/wasm-bindgen` to enable + `getrandom` support on `wasm32-unknown-unknown` + +Additionally, these features configure Rand: + +- `small_rng` enables inclusion of the `SmallRng` PRNG +- `nightly` enables all experimental features +- `simd_support` (experimental) enables sampling of SIMD values + (uniformly random SIMD integers and floats) +Rand supports limited functionality in `no_std` mode (enabled via +`default-features = false`). In this case, `OsRng` and `from_entropy` are +unavailable (unless `getrandom` is enabled), large parts of `seq` are +unavailable (unless `alloc` is enabled), and `thread_rng` and `random` are +unavailable. # License diff --git a/rand/appveyor.yml b/rand/appveyor.yml index 70e4326..ef0b4bf 100644 --- a/rand/appveyor.yml +++ b/rand/appveyor.yml @@ -32,20 +32,14 @@ install: build: false test_script: - - cargo test --lib --no-default-features --features alloc + - cargo test --tests --no-default-features --features alloc # TODO: use --all-features once simd_support is sufficiently stable: - cargo test --features=serde1,log - cargo test --benches --features=nightly - cargo test --examples - - cargo test --package rand_core - - cargo test --package rand_core --no-default-features --features=alloc - - cargo test --package rand_isaac --features=serde1 - - cargo test --package rand_xorshift --features=serde1 - - cargo test --package rand_xoshiro - - cargo test --package rand_chacha - - cargo test --package rand_hc - cargo test --manifest-path rand_core/Cargo.toml - cargo test --manifest-path rand_core/Cargo.toml --no-default-features --features=alloc + - cargo test --manifest-path rand_distr/Cargo.toml - cargo test --manifest-path rand_isaac/Cargo.toml --features=serde1 - cargo test --manifest-path rand_pcg/Cargo.toml --features=serde1 - cargo test --manifest-path rand_xorshift/Cargo.toml --features=serde1 diff --git a/rand/benches/generators.rs b/rand/benches/generators.rs index a6e3a42..808bb67 100644 --- a/rand/benches/generators.rs +++ b/rand/benches/generators.rs @@ -7,15 +7,9 @@ // except according to those terms. #![feature(test)] +#![allow(non_snake_case)] extern crate test; -extern crate rand; -extern crate rand_isaac; -extern crate rand_chacha; -extern crate rand_hc; -extern crate rand_pcg; -extern crate rand_xorshift; -extern crate rand_xoshiro; const RAND_BENCH_N: u64 = 1000; const BYTES_LEN: usize = 1024; @@ -25,11 +19,11 @@ use test::{black_box, Bencher}; use rand::prelude::*; use rand::rngs::adapter::ReseedingRng; -use rand::rngs::{OsRng, JitterRng, EntropyRng}; +use rand::rngs::{OsRng, mock::StepRng}; use rand_isaac::{IsaacRng, Isaac64Rng}; -use rand_chacha::ChaChaRng; -use rand_hc::{Hc128Rng, Hc128Core}; -use rand_pcg::{Lcg64Xsh32, Mcg128Xsl64}; +use rand_chacha::{ChaCha20Core, ChaCha8Rng, ChaCha12Rng, ChaCha20Rng}; +use rand_hc::{Hc128Rng}; +use rand_pcg::{Pcg32, Pcg64, Pcg64Mcg}; use rand_xorshift::XorShiftRng; use rand_xoshiro::{Xoshiro256StarStar, Xoshiro256Plus, Xoshiro128StarStar, Xoshiro128Plus, Xoroshiro128StarStar, Xoroshiro128Plus, SplitMix64, @@ -52,6 +46,7 @@ macro_rules! gen_bytes { } } +gen_bytes!(gen_bytes_step, StepRng::new(0, 1)); gen_bytes!(gen_bytes_xorshift, XorShiftRng::from_entropy()); gen_bytes!(gen_bytes_xoshiro256starstar, Xoshiro256StarStar::from_entropy()); gen_bytes!(gen_bytes_xoshiro256plus, Xoshiro256Plus::from_entropy()); @@ -62,15 +57,19 @@ gen_bytes!(gen_bytes_xoroshiro128plus, Xoroshiro128Plus::from_entropy()); gen_bytes!(gen_bytes_xoroshiro64starstar, Xoroshiro64StarStar::from_entropy()); gen_bytes!(gen_bytes_xoroshiro64star, Xoroshiro64Star::from_entropy()); gen_bytes!(gen_bytes_splitmix64, SplitMix64::from_entropy()); -gen_bytes!(gen_bytes_lcg64_xsh32, Lcg64Xsh32::from_entropy()); -gen_bytes!(gen_bytes_mcg128_xsh64, Mcg128Xsl64::from_entropy()); -gen_bytes!(gen_bytes_chacha20, ChaChaRng::from_entropy()); +gen_bytes!(gen_bytes_pcg32, Pcg32::from_entropy()); +gen_bytes!(gen_bytes_pcg64, Pcg64::from_entropy()); +gen_bytes!(gen_bytes_pcg64mcg, Pcg64Mcg::from_entropy()); +gen_bytes!(gen_bytes_chacha8, ChaCha8Rng::from_entropy()); +gen_bytes!(gen_bytes_chacha12, ChaCha12Rng::from_entropy()); +gen_bytes!(gen_bytes_chacha20, ChaCha20Rng::from_entropy()); gen_bytes!(gen_bytes_hc128, Hc128Rng::from_entropy()); gen_bytes!(gen_bytes_isaac, IsaacRng::from_entropy()); gen_bytes!(gen_bytes_isaac64, Isaac64Rng::from_entropy()); gen_bytes!(gen_bytes_std, StdRng::from_entropy()); +#[cfg(feature="small_rng")] gen_bytes!(gen_bytes_small, SmallRng::from_entropy()); -gen_bytes!(gen_bytes_os, OsRng::new().unwrap()); +gen_bytes!(gen_bytes_os, OsRng); macro_rules! gen_uint { ($fnn:ident, $ty:ty, $gen:expr) => { @@ -89,6 +88,7 @@ macro_rules! gen_uint { } } +gen_uint!(gen_u32_step, u32, StepRng::new(0, 1)); gen_uint!(gen_u32_xorshift, u32, XorShiftRng::from_entropy()); gen_uint!(gen_u32_xoshiro256starstar, u32, Xoshiro256StarStar::from_entropy()); gen_uint!(gen_u32_xoshiro256plus, u32, Xoshiro256Plus::from_entropy()); @@ -99,16 +99,21 @@ gen_uint!(gen_u32_xoroshiro128plus, u32, Xoroshiro128Plus::from_entropy()); gen_uint!(gen_u32_xoroshiro64starstar, u32, Xoroshiro64StarStar::from_entropy()); gen_uint!(gen_u32_xoroshiro64star, u32, Xoroshiro64Star::from_entropy()); gen_uint!(gen_u32_splitmix64, u32, SplitMix64::from_entropy()); -gen_uint!(gen_u32_lcg64_xsh32, u32, Lcg64Xsh32::from_entropy()); -gen_uint!(gen_u32_mcg128_xsh64, u32, Mcg128Xsl64::from_entropy()); -gen_uint!(gen_u32_chacha20, u32, ChaChaRng::from_entropy()); +gen_uint!(gen_u32_pcg32, u32, Pcg32::from_entropy()); +gen_uint!(gen_u32_pcg64, u32, Pcg64::from_entropy()); +gen_uint!(gen_u32_pcg64mcg, u32, Pcg64Mcg::from_entropy()); +gen_uint!(gen_u32_chacha8, u32, ChaCha8Rng::from_entropy()); +gen_uint!(gen_u32_chacha12, u32, ChaCha12Rng::from_entropy()); +gen_uint!(gen_u32_chacha20, u32, ChaCha20Rng::from_entropy()); gen_uint!(gen_u32_hc128, u32, Hc128Rng::from_entropy()); gen_uint!(gen_u32_isaac, u32, IsaacRng::from_entropy()); gen_uint!(gen_u32_isaac64, u32, Isaac64Rng::from_entropy()); gen_uint!(gen_u32_std, u32, StdRng::from_entropy()); +#[cfg(feature="small_rng")] gen_uint!(gen_u32_small, u32, SmallRng::from_entropy()); -gen_uint!(gen_u32_os, u32, OsRng::new().unwrap()); +gen_uint!(gen_u32_os, u32, OsRng); +gen_uint!(gen_u64_step, u64, StepRng::new(0, 1)); gen_uint!(gen_u64_xorshift, u64, XorShiftRng::from_entropy()); gen_uint!(gen_u64_xoshiro256starstar, u64, Xoshiro256StarStar::from_entropy()); gen_uint!(gen_u64_xoshiro256plus, u64, Xoshiro256Plus::from_entropy()); @@ -119,26 +124,19 @@ gen_uint!(gen_u64_xoroshiro128plus, u64, Xoroshiro128Plus::from_entropy()); gen_uint!(gen_u64_xoroshiro64starstar, u64, Xoroshiro64StarStar::from_entropy()); gen_uint!(gen_u64_xoroshiro64star, u64, Xoroshiro64Star::from_entropy()); gen_uint!(gen_u64_splitmix64, u64, SplitMix64::from_entropy()); -gen_uint!(gen_u64_lcg64_xsh32, u64, Lcg64Xsh32::from_entropy()); -gen_uint!(gen_u64_mcg128_xsh64, u64, Mcg128Xsl64::from_entropy()); -gen_uint!(gen_u64_chacha20, u64, ChaChaRng::from_entropy()); +gen_uint!(gen_u64_pcg32, u64, Pcg32::from_entropy()); +gen_uint!(gen_u64_pcg64, u64, Pcg64::from_entropy()); +gen_uint!(gen_u64_pcg64mcg, u64, Pcg64Mcg::from_entropy()); +gen_uint!(gen_u64_chacha8, u64, ChaCha8Rng::from_entropy()); +gen_uint!(gen_u64_chacha12, u64, ChaCha12Rng::from_entropy()); +gen_uint!(gen_u64_chacha20, u64, ChaCha20Rng::from_entropy()); gen_uint!(gen_u64_hc128, u64, Hc128Rng::from_entropy()); gen_uint!(gen_u64_isaac, u64, IsaacRng::from_entropy()); gen_uint!(gen_u64_isaac64, u64, Isaac64Rng::from_entropy()); gen_uint!(gen_u64_std, u64, StdRng::from_entropy()); +#[cfg(feature="small_rng")] gen_uint!(gen_u64_small, u64, SmallRng::from_entropy()); -gen_uint!(gen_u64_os, u64, OsRng::new().unwrap()); - -// Do not test JitterRng like the others by running it RAND_BENCH_N times per, -// measurement, because it is way too slow. Only run it once. -#[bench] -fn gen_u64_jitter(b: &mut Bencher) { - let mut rng = JitterRng::new().unwrap(); - b.iter(|| { - rng.gen::<u64>() - }); - b.bytes = size_of::<u64>() as u64; -} +gen_uint!(gen_u64_os, u64, OsRng); macro_rules! init_gen { ($fnn:ident, $gen:ident) => { @@ -163,60 +161,42 @@ init_gen!(init_xoroshiro128plus, Xoroshiro128Plus); init_gen!(init_xoroshiro64starstar, Xoroshiro64StarStar); init_gen!(init_xoroshiro64star, Xoroshiro64Star); init_gen!(init_splitmix64, SplitMix64); -init_gen!(init_lcg64_xsh32, Lcg64Xsh32); -init_gen!(init_mcg128_xsh64, Mcg128Xsl64); +init_gen!(init_pcg32, Pcg32); +init_gen!(init_pcg64, Pcg64); +init_gen!(init_pcg64mcg, Pcg64Mcg); init_gen!(init_hc128, Hc128Rng); init_gen!(init_isaac, IsaacRng); init_gen!(init_isaac64, Isaac64Rng); -init_gen!(init_chacha, ChaChaRng); - -#[bench] -fn init_jitter(b: &mut Bencher) { - b.iter(|| { - JitterRng::new().unwrap() - }); -} - +init_gen!(init_chacha, ChaCha20Rng); -const RESEEDING_THRESHOLD: u64 = 1024*1024*1024; // something high enough to get - // deterministic measurements - -#[bench] -fn reseeding_hc128_bytes(b: &mut Bencher) { - let mut rng = ReseedingRng::new(Hc128Core::from_entropy(), - RESEEDING_THRESHOLD, - EntropyRng::new()); - let mut buf = [0u8; BYTES_LEN]; - b.iter(|| { - for _ in 0..RAND_BENCH_N { - rng.fill_bytes(&mut buf); - black_box(buf); - } - }); - b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; -} +const RESEEDING_BYTES_LEN: usize = 1024 * 1024; +const RESEEDING_BENCH_N: u64 = 16; -macro_rules! reseeding_uint { - ($fnn:ident, $ty:ty) => { +macro_rules! reseeding_bytes { + ($fnn:ident, $thresh:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = ReseedingRng::new(Hc128Core::from_entropy(), - RESEEDING_THRESHOLD, - EntropyRng::new()); + let mut rng = ReseedingRng::new(ChaCha20Core::from_entropy(), + $thresh * 1024, + OsRng); + let mut buf = [0u8; RESEEDING_BYTES_LEN]; b.iter(|| { - let mut accum: $ty = 0; - for _ in 0..RAND_BENCH_N { - accum = accum.wrapping_add(rng.gen::<$ty>()); + for _ in 0..RESEEDING_BENCH_N { + rng.fill_bytes(&mut buf); + black_box(&buf); } - accum }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; + b.bytes = RESEEDING_BYTES_LEN as u64 * RESEEDING_BENCH_N; } } } -reseeding_uint!(reseeding_hc128_u32, u32); -reseeding_uint!(reseeding_hc128_u64, u64); +reseeding_bytes!(reseeding_chacha20_4k, 4); +reseeding_bytes!(reseeding_chacha20_16k, 16); +reseeding_bytes!(reseeding_chacha20_32k, 32); +reseeding_bytes!(reseeding_chacha20_64k, 64); +reseeding_bytes!(reseeding_chacha20_256k, 256); +reseeding_bytes!(reseeding_chacha20_1M, 1024); macro_rules! threadrng_uint { diff --git a/rand/benches/misc.rs b/rand/benches/misc.rs index 8fb3a83..4098686 100644 --- a/rand/benches/misc.rs +++ b/rand/benches/misc.rs @@ -9,20 +9,21 @@ #![feature(test)] extern crate test; -extern crate rand; const RAND_BENCH_N: u64 = 1000; use test::Bencher; use rand::prelude::*; +use rand::distributions::{Distribution, Standard, Bernoulli}; +use rand_pcg::{Pcg32, Pcg64Mcg}; #[bench] fn misc_gen_bool_const(b: &mut Bencher) { - let mut rng = StdRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg32::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { let mut accum = true; - for _ in 0..::RAND_BENCH_N { + for _ in 0..crate::RAND_BENCH_N { accum ^= rng.gen_bool(0.18); } accum @@ -31,11 +32,11 @@ fn misc_gen_bool_const(b: &mut Bencher) { #[bench] fn misc_gen_bool_var(b: &mut Bencher) { - let mut rng = StdRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg32::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { let mut accum = true; let mut p = 0.18; - for _ in 0..::RAND_BENCH_N { + for _ in 0..crate::RAND_BENCH_N { accum ^= rng.gen_bool(p); p += 0.0001; } @@ -45,10 +46,10 @@ fn misc_gen_bool_var(b: &mut Bencher) { #[bench] fn misc_gen_ratio_const(b: &mut Bencher) { - let mut rng = StdRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg32::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { let mut accum = true; - for _ in 0..::RAND_BENCH_N { + for _ in 0..crate::RAND_BENCH_N { accum ^= rng.gen_ratio(2, 3); } accum @@ -57,10 +58,10 @@ fn misc_gen_ratio_const(b: &mut Bencher) { #[bench] fn misc_gen_ratio_var(b: &mut Bencher) { - let mut rng = StdRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg32::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { let mut accum = true; - for i in 2..(::RAND_BENCH_N as u32 + 2) { + for i in 2..(crate::RAND_BENCH_N as u32 + 2) { accum ^= rng.gen_ratio(i, i + 1); } accum @@ -69,11 +70,11 @@ fn misc_gen_ratio_var(b: &mut Bencher) { #[bench] fn misc_bernoulli_const(b: &mut Bencher) { - let mut rng = StdRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg32::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { - let d = rand::distributions::Bernoulli::new(0.18); + let d = rand::distributions::Bernoulli::new(0.18).unwrap(); let mut accum = true; - for _ in 0..::RAND_BENCH_N { + for _ in 0..crate::RAND_BENCH_N { accum ^= rng.sample(d); } accum @@ -82,12 +83,12 @@ fn misc_bernoulli_const(b: &mut Bencher) { #[bench] fn misc_bernoulli_var(b: &mut Bencher) { - let mut rng = StdRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg32::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { let mut accum = true; let mut p = 0.18; - for _ in 0..::RAND_BENCH_N { - let d = rand::distributions::Bernoulli::new(p); + for _ in 0..crate::RAND_BENCH_N { + let d = Bernoulli::new(p).unwrap(); accum ^= rng.sample(d); p += 0.0001; } @@ -95,30 +96,10 @@ fn misc_bernoulli_var(b: &mut Bencher) { }) } -macro_rules! sample_binomial { - ($name:ident, $n:expr, $p:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); - let (n, p) = ($n, $p); - b.iter(|| { - let d = rand::distributions::Binomial::new(n, p); - rng.sample(d) - }) - } - } -} - -sample_binomial!(misc_binomial_1, 1, 0.9); -sample_binomial!(misc_binomial_10, 10, 0.9); -sample_binomial!(misc_binomial_100, 100, 0.99); -sample_binomial!(misc_binomial_1000, 1000, 0.01); -sample_binomial!(misc_binomial_1e12, 1000_000_000_000, 0.2); - #[bench] fn gen_1k_iter_repeat(b: &mut Bencher) { use std::iter; - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { let v: Vec<u64> = iter::repeat(()).map(|()| rng.gen()).take(128).collect(); v @@ -128,8 +109,7 @@ fn gen_1k_iter_repeat(b: &mut Bencher) { #[bench] fn gen_1k_sample_iter(b: &mut Bencher) { - use rand::distributions::{Distribution, Standard}; - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { let v: Vec<u64> = Standard.sample_iter(&mut rng).take(128).collect(); v @@ -139,7 +119,7 @@ fn gen_1k_sample_iter(b: &mut Bencher) { #[bench] fn gen_1k_gen_array(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()).unwrap(); b.iter(|| { // max supported array length is 32! let v: [[u64; 32]; 4] = rng.gen(); @@ -150,7 +130,7 @@ fn gen_1k_gen_array(b: &mut Bencher) { #[bench] fn gen_1k_fill(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()).unwrap(); let mut buf = [0u64; 128]; b.iter(|| { rng.fill(&mut buf[..]); diff --git a/rand/benches/seq.rs b/rand/benches/seq.rs index 0ca3398..4c671b8 100644 --- a/rand/benches/seq.rs +++ b/rand/benches/seq.rs @@ -10,7 +10,6 @@ #![allow(non_snake_case)] extern crate test; -extern crate rand; use test::Bencher; @@ -18,6 +17,10 @@ use rand::prelude::*; use rand::seq::*; use std::mem::size_of; +// We force use of 32-bit RNG since seq code is optimised for use with 32-bit +// generators on all platforms. +use rand_pcg::Pcg32 as SmallRng; + const RAND_BENCH_N: u64 = 1000; #[bench] @@ -44,7 +47,7 @@ fn seq_slice_choose_1_of_1000(b: &mut Bencher) { } s }); - b.bytes = size_of::<usize>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<usize>() as u64 * crate::RAND_BENCH_N; } macro_rules! seq_slice_choose_multiple { @@ -86,7 +89,7 @@ fn seq_iter_choose_from_1000(b: &mut Bencher) { } s }); - b.bytes = size_of::<usize>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<usize>() as u64 * crate::RAND_BENCH_N; } #[derive(Clone)] diff --git a/rand/benches/weighted.rs b/rand/benches/weighted.rs new file mode 100644 index 0000000..5ddca3f --- /dev/null +++ b/rand/benches/weighted.rs @@ -0,0 +1,36 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(test)] + +extern crate test; + +use test::Bencher; +use rand::Rng; +use rand::distributions::WeightedIndex; + +#[bench] +fn weighted_index_creation(b: &mut Bencher) { + let mut rng = rand::thread_rng(); + let weights = [1u32, 2, 4, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 7]; + b.iter(|| { + let distr = WeightedIndex::new(weights.to_vec()).unwrap(); + rng.sample(distr) + }) +} + +#[bench] +fn weighted_index_modification(b: &mut Bencher) { + let mut rng = rand::thread_rng(); + let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]; + let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); + b.iter(|| { + distr.update_weights(&[(2, &4), (5, &1)]).unwrap(); + rng.sample(&distr) + }) +} diff --git a/rand/build.rs b/rand/build.rs deleted file mode 100644 index a554ad9..0000000 --- a/rand/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -extern crate autocfg; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let ac = autocfg::new(); - ac.emit_rustc_version(1, 25); - ac.emit_rustc_version(1, 26); - ac.emit_rustc_version(1, 27); -} diff --git a/rand/examples/monte-carlo.rs b/rand/examples/monte-carlo.rs index 9162996..39c779f 100644 --- a/rand/examples/monte-carlo.rs +++ b/rand/examples/monte-carlo.rs @@ -11,7 +11,7 @@ //! //! Imagine that we have a square with sides of length 2 and a unit circle //! (radius = 1), both centered at the origin. The areas are: -//! +//! //! ```text //! area of circle = πr² = π * r * r = π //! area of square = 2² = 4 @@ -24,28 +24,25 @@ //! the square at random, calculate the fraction that fall within the circle, //! and multiply this fraction by 4. -#![cfg(feature="std")] - - -extern crate rand; +#![cfg(feature = "std")] use rand::distributions::{Distribution, Uniform}; fn main() { - let range = Uniform::new(-1.0f64, 1.0); - let mut rng = rand::thread_rng(); - - let total = 1_000_000; - let mut in_circle = 0; - - for _ in 0..total { - let a = range.sample(&mut rng); - let b = range.sample(&mut rng); - if a*a + b*b <= 1.0 { - in_circle += 1; - } - } - - // prints something close to 3.14159... - println!("π is approximately {}", 4. * (in_circle as f64) / (total as f64)); + let range = Uniform::new(-1.0f64, 1.0); + let mut rng = rand::thread_rng(); + + let total = 1_000_000; + let mut in_circle = 0; + + for _ in 0..total { + let a = range.sample(&mut rng); + let b = range.sample(&mut rng); + if a*a + b*b <= 1.0 { + in_circle += 1; + } + } + + // prints something close to 3.14159... + println!("π is approximately {}", 4. * (in_circle as f64) / (total as f64)); } diff --git a/rand/examples/monty-hall.rs b/rand/examples/monty-hall.rs index 0932c5e..9fe5839 100644 --- a/rand/examples/monty-hall.rs +++ b/rand/examples/monty-hall.rs @@ -26,13 +26,10 @@ //! //! [Monty Hall Problem]: https://en.wikipedia.org/wiki/Monty_Hall_problem -#![cfg(feature="std")] +#![cfg(feature = "std")] - -extern crate rand; - -use rand::Rng; use rand::distributions::{Distribution, Uniform}; +use rand::Rng; struct SimulationResult { win: bool, @@ -40,8 +37,7 @@ struct SimulationResult { } // Run a single simulation of the Monty Hall problem. -fn simulate<R: Rng>(random_door: &Uniform<u32>, rng: &mut R) - -> SimulationResult { +fn simulate<R: Rng>(random_door: &Uniform<u32>, rng: &mut R) -> SimulationResult { let car = random_door.sample(rng); // This is our initial choice diff --git a/rand/rand_chacha/CHANGELOG.md b/rand/rand_chacha/CHANGELOG.md index a1979f6..d242f97 100644 --- a/rand/rand_chacha/CHANGELOG.md +++ b/rand/rand_chacha/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.1] - 2019-07-22 +- Force enable the `simd` feature of `c2-chacha` (#845) + +## [0.2.0] - 2019-06-06 +- Rewrite based on the much faster `c2-chacha` crate (#789) + ## [0.1.1] - 2019-01-04 - Disable `i128` and `u128` if the `target_os` is `emscripten` (#671: work-around Emscripten limitation) - Update readme and doc links diff --git a/rand/rand_chacha/Cargo.toml b/rand/rand_chacha/Cargo.toml index 028428c..6a47b86 100644 --- a/rand/rand_chacha/Cargo.toml +++ b/rand/rand_chacha/Cargo.toml @@ -1,25 +1,28 @@ [package] name = "rand_chacha" -version = "0.1.1" -authors = ["The Rand Project Developers", "The Rust Project Developers"] -license = "MIT/Apache-2.0" +version = "0.2.1" +authors = ["The Rand Project Developers", "The Rust Project Developers", "The CryptoCorrosion Contributors"] +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" -documentation = "https://rust-random.github.io/rand/rand_chacha" +documentation = "https://rust-random.github.io/rand/rand_chacha/" homepage = "https://crates.io/crates/rand_chacha" description = """ ChaCha random number generator """ keywords = ["random", "rng", "chacha"] categories = ["algorithms", "no-std"] -build = "build.rs" +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } [dependencies] -rand_core = { path = "../rand_core", version = ">=0.2, <0.4", default-features=false } +rand_core = { path = "../rand_core", version = "0.5" } +c2-chacha = { version = "0.2.2", default-features = false, features = ["simd"] } -[build-dependencies] -autocfg = "0.1" +[features] +default = ["std", "simd"] +std = ["c2-chacha/std"] +simd = [] # deprecated diff --git a/rand/rand_chacha/README.md b/rand/rand_chacha/README.md index 5a1dbac..69a0ce7 100644 --- a/rand/rand_chacha/README.md +++ b/rand/rand_chacha/README.md @@ -6,7 +6,7 @@ [![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_chacha) [![API](https://docs.rs/rand_chacha/badge.svg)](https://docs.rs/rand_chacha) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) A cryptographically secure random number generator that uses the ChaCha algorithm. @@ -16,11 +16,14 @@ as an RNG. It is an improved variant of the Salsa20 cipher family, which was selected as one of the "stream ciphers suitable for widespread adoption" by eSTREAM[^2]. +The RNGs provided by this crate are implemented via the fast stream ciphers of +the [`c2-chacha`](https://crates.io/crates/c2-chacha) crate. + Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_chacha) - [API documentation (docs.rs)](https://docs.rs/rand_chacha) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_chacha/CHANGELOG.md) [rand]: https://crates.io/crates/rand [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*]( @@ -32,8 +35,9 @@ Links: ## Crate Features -`rand_chacha` is `no_std` compatible. It does not require any functionality -outside of the `core` lib, thus there are no features to configure. +`rand_chacha` is `no_std` compatible when disabling default features; the `std` +feature can be explicitly required to re-enable `std` support. Using `std` +allows detection of CPU features and thus better optimisation. # License diff --git a/rand/rand_chacha/build.rs b/rand/rand_chacha/build.rs deleted file mode 100644 index 06e12a4..0000000 --- a/rand/rand_chacha/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern crate autocfg; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - let ac = autocfg::new(); - ac.emit_rustc_version(1, 26); -} diff --git a/rand/rand_chacha/src/chacha.rs b/rand/rand_chacha/src/chacha.rs index 86f191e..b1b89e0 100644 --- a/rand/rand_chacha/src/chacha.rs +++ b/rand/rand_chacha/src/chacha.rs @@ -1,5 +1,4 @@ // Copyright 2018 Developers of the Rand project. -// Copyright 2014 The Rust Project Developers. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license @@ -9,274 +8,251 @@ //! The ChaCha random number generator. -use core::fmt; -use rand_core::{CryptoRng, RngCore, SeedableRng, Error, le}; -use rand_core::block::{BlockRngCore, BlockRng}; - -const SEED_WORDS: usize = 8; // 8 words for the 256-bit key -const STATE_WORDS: usize = 16; - -/// A cryptographically secure random number generator that uses the ChaCha -/// algorithm. -/// -/// ChaCha is a stream cipher designed by Daniel J. Bernstein[^1], that we use -/// as an RNG. It is an improved variant of the Salsa20 cipher family, which was -/// selected as one of the "stream ciphers suitable for widespread adoption" by -/// eSTREAM[^2]. -/// -/// ChaCha uses add-rotate-xor (ARX) operations as its basis. These are safe -/// against timing attacks, although that is mostly a concern for ciphers and -/// not for RNGs. Also it is very suitable for SIMD implementation. -/// Here we do not provide a SIMD implementation yet, except for what is -/// provided by auto-vectorisation. -/// -/// With the ChaCha algorithm it is possible to choose the number of rounds the -/// core algorithm should run. The number of rounds is a tradeoff between -/// performance and security, where 8 rounds is the minimum potentially -/// secure configuration, and 20 rounds is widely used as a conservative choice. -/// We use 20 rounds in this implementation, but hope to allow type-level -/// configuration in the future. -/// -/// We use a 64-bit counter and 64-bit stream identifier as in Bernstein's -/// implementation[^1] except that we use a stream identifier in place of a -/// nonce. A 64-bit counter over 64-byte (16 word) blocks allows 1 ZiB of output -/// before cycling, and the stream identifier allows 2<sup>64</sup> unique -/// streams of output per seed. Both counter and stream are initialized to zero -/// but may be set via [`set_word_pos`] and [`set_stream`]. -/// -/// The word layout is: -/// -/// ```text -/// constant constant constant constant -/// seed seed seed seed -/// seed seed seed seed -/// counter counter stream_id stream_id -/// ``` -/// -/// This implementation uses an output buffer of sixteen `u32` words, and uses -/// [`BlockRng`] to implement the [`RngCore`] methods. -/// -/// [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*]( -/// https://cr.yp.to/chacha.html) -/// -/// [^2]: [eSTREAM: the ECRYPT Stream Cipher Project]( -/// http://www.ecrypt.eu.org/stream/) -/// -/// [`set_word_pos`]: #method.set_word_pos -/// [`set_stream`]: #method.set_stream -/// [`BlockRng`]: ../rand_core/block/struct.BlockRng.html -/// [`RngCore`]: ../rand_core/trait.RngCore.html -#[derive(Clone, Debug)] -pub struct ChaChaRng(BlockRng<ChaChaCore>); - -impl RngCore for ChaChaRng { - #[inline] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() +#[cfg(feature = "std")] +use std as core; +#[cfg(not(feature = "std"))] +use core; + +use c2_chacha::guts::ChaCha; +use self::core::fmt; +use rand_core::block::{BlockRng, BlockRngCore}; +use rand_core::{CryptoRng, Error, RngCore, SeedableRng}; + +const STREAM_PARAM_NONCE: u32 = 1; +const STREAM_PARAM_BLOCK: u32 = 0; + +pub struct Array64<T>([T; 64]); +impl<T> Default for Array64<T> where T: Default { + fn default() -> Self { + Self([T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), + T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), + T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), + T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), + T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), + T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), + T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), + T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default()]) } - - #[inline] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest) +} +impl<T> AsRef<[T]> for Array64<T> { + fn as_ref(&self) -> &[T] { + &self.0 } - - #[inline] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) +} +impl<T> AsMut<[T]> for Array64<T> { + fn as_mut(&mut self) -> &mut [T] { + &mut self.0 } } - -impl SeedableRng for ChaChaRng { - type Seed = <ChaChaCore as SeedableRng>::Seed; - - fn from_seed(seed: Self::Seed) -> Self { - ChaChaRng(BlockRng::<ChaChaCore>::from_seed(seed)) +impl<T> Clone for Array64<T> where T: Copy + Default { + fn clone(&self) -> Self { + let mut new = Self::default(); + new.0.copy_from_slice(&self.0); + new } - - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - BlockRng::<ChaChaCore>::from_rng(rng).map(ChaChaRng) +} +impl<T> fmt::Debug for Array64<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Array64 {{}}") } } -impl CryptoRng for ChaChaRng {} - -impl ChaChaRng { - /// Get the offset from the start of the stream, in 32-bit words. - /// - /// Since the generated blocks are 16 words (2<sup>4</sup>) long and the - /// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are - /// not supported, hence the result can simply be multiplied by 4 to get a - /// byte-offset. - /// - /// Note: this function is currently only available with Rust 1.26 or later. - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] - pub fn get_word_pos(&self) -> u128 { - let mut c = (self.0.core.state[13] as u64) << 32 - | (self.0.core.state[12] as u64); - let mut index = self.0.index(); - // c is the end of the last block generated, unless index is at end - if index >= STATE_WORDS { - index = 0; - } else { - c = c.wrapping_sub(1); +macro_rules! chacha_impl { + ($ChaChaXCore:ident, $ChaChaXRng:ident, $rounds:expr, $doc:expr) => { + #[doc=$doc] + #[derive(Clone)] + pub struct $ChaChaXCore { + state: ChaCha, } - ((c as u128) << 4) | (index as u128) - } - /// Set the offset from the start of the stream, in 32-bit words. - /// - /// As with `get_word_pos`, we use a 68-bit number. Since the generator - /// simply cycles at the end of its period (1 ZiB), we ignore the upper - /// 60 bits. - /// - /// Note: this function is currently only available with Rust 1.26 or later. - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] - pub fn set_word_pos(&mut self, word_offset: u128) { - let index = (word_offset as usize) & 0xF; - let counter = (word_offset >> 4) as u64; - self.0.core.state[12] = counter as u32; - self.0.core.state[13] = (counter >> 32) as u32; - if index != 0 { - self.0.generate_and_set(index); // also increments counter - } else { - self.0.reset(); + // Custom Debug implementation that does not expose the internal state + impl fmt::Debug for $ChaChaXCore { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChaChaXCore {{}}") + } } - } - /// Set the stream number. - /// - /// This is initialized to zero; 2<sup>64</sup> unique streams of output - /// are available per seed/key. - /// - /// Note that in order to reproduce ChaCha output with a specific 64-bit - /// nonce, one can convert that nonce to a `u64` in little-endian fashion - /// and pass to this function. In theory a 96-bit nonce can be used by - /// passing the last 64-bits to this function and using the first 32-bits as - /// the most significant half of the 64-bit counter (which may be set - /// indirectly via `set_word_pos`), but this is not directly supported. - pub fn set_stream(&mut self, stream: u64) { - let index = self.0.index(); - self.0.core.state[14] = stream as u32; - self.0.core.state[15] = (stream >> 32) as u32; - if index < STATE_WORDS { - // we need to regenerate a partial result buffer - { - // reverse of counter adjustment in generate() - if self.0.core.state[12] == 0 { - self.0.core.state[13] = self.0.core.state[13].wrapping_sub(1); + impl BlockRngCore for $ChaChaXCore { + type Item = u32; + type Results = Array64<u32>; + #[inline] + fn generate(&mut self, r: &mut Self::Results) { + // Fill slice of words by writing to equivalent slice of bytes, then fixing endianness. + self.state.refill4($rounds, unsafe { + &mut *(&mut *r as *mut Array64<u32> as *mut [u8; 256]) + }); + for x in r.as_mut() { + *x = x.to_le(); } - self.0.core.state[12] = self.0.core.state[12].wrapping_sub(1); } - self.0.generate_and_set(index); } - } -} -/// The core of `ChaChaRng`, used with `BlockRng`. -#[derive(Clone)] -pub struct ChaChaCore { - state: [u32; STATE_WORDS], -} - -// Custom Debug implementation that does not expose the internal state -impl fmt::Debug for ChaChaCore { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ChaChaCore {{}}") - } -} + impl SeedableRng for $ChaChaXCore { + type Seed = [u8; 32]; + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + $ChaChaXCore { state: ChaCha::new(&seed, &[0u8; 8]) } + } + } -macro_rules! quarter_round{ - ($a: expr, $b: expr, $c: expr, $d: expr) => {{ - $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left(16); - $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left(12); - $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left( 8); - $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left( 7); - }} -} + /// A cryptographically secure random number generator that uses the ChaCha algorithm. + /// + /// ChaCha is a stream cipher designed by Daniel J. Bernstein[^1], that we use as an RNG. It is + /// an improved variant of the Salsa20 cipher family, which was selected as one of the "stream + /// ciphers suitable for widespread adoption" by eSTREAM[^2]. + /// + /// ChaCha uses add-rotate-xor (ARX) operations as its basis. These are safe against timing + /// attacks, although that is mostly a concern for ciphers and not for RNGs. We provide a SIMD + /// implementation to support high throughput on a variety of common hardware platforms. + /// + /// With the ChaCha algorithm it is possible to choose the number of rounds the core algorithm + /// should run. The number of rounds is a tradeoff between performance and security, where 8 + /// rounds is the minimum potentially secure configuration, and 20 rounds is widely used as a + /// conservative choice. + /// + /// We use a 64-bit counter and 64-bit stream identifier as in Bernstein's implementation[^1] + /// except that we use a stream identifier in place of a nonce. A 64-bit counter over 64-byte + /// (16 word) blocks allows 1 ZiB of output before cycling, and the stream identifier allows + /// 2<sup>64</sup> unique streams of output per seed. Both counter and stream are initialized + /// to zero but may be set via the `set_word_pos` and `set_stream` methods. + /// + /// The word layout is: + /// + /// ```text + /// constant constant constant constant + /// seed seed seed seed + /// seed seed seed seed + /// counter counter stream_id stream_id + /// ``` + /// + /// This implementation uses an output buffer of sixteen `u32` words, and uses + /// [`BlockRng`] to implement the [`RngCore`] methods. + /// + /// [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*]( + /// https://cr.yp.to/chacha.html) + /// + /// [^2]: [eSTREAM: the ECRYPT Stream Cipher Project]( + /// http://www.ecrypt.eu.org/stream/) + #[derive(Clone, Debug)] + pub struct $ChaChaXRng { + rng: BlockRng<$ChaChaXCore>, + } -macro_rules! double_round{ - ($x: expr) => {{ - // Column round - quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]); - quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]); - quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]); - quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]); - // Diagonal round - quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]); - quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]); - quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]); - quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]); - }} -} + impl SeedableRng for $ChaChaXRng { + type Seed = [u8; 32]; + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + let core = $ChaChaXCore::from_seed(seed); + Self { + rng: BlockRng::new(core), + } + } + } -impl BlockRngCore for ChaChaCore { - type Item = u32; - type Results = [u32; STATE_WORDS]; - - fn generate(&mut self, results: &mut Self::Results) { - // For some reason extracting this part into a separate function - // improves performance by 50%. - fn core(results: &mut [u32; STATE_WORDS], - state: &[u32; STATE_WORDS]) - { - let mut tmp = *state; - let rounds = 20; - for _ in 0..rounds / 2 { - double_round!(tmp); + impl RngCore for $ChaChaXRng { + #[inline] + fn next_u32(&mut self) -> u32 { + self.rng.next_u32() + } + #[inline] + fn next_u64(&mut self) -> u64 { + self.rng.next_u64() } - for i in 0..STATE_WORDS { - results[i] = tmp[i].wrapping_add(state[i]); + #[inline] + fn fill_bytes(&mut self, bytes: &mut [u8]) { + self.rng.fill_bytes(bytes) + } + #[inline] + fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + self.rng.try_fill_bytes(bytes) } } - core(results, &self.state); + impl $ChaChaXRng { + // The buffer is a 4-block window, i.e. it is always at a block-aligned position in the + // stream but if the stream has been seeked it may not be self-aligned. + + /// Get the offset from the start of the stream, in 32-bit words. + /// + /// Since the generated blocks are 16 words (2<sup>4</sup>) long and the + /// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are + /// not supported, hence the result can simply be multiplied by 4 to get a + /// byte-offset. + #[inline] + pub fn get_word_pos(&self) -> u128 { + let mut block = u128::from(self.rng.core.state.get_stream_param(STREAM_PARAM_BLOCK)); + // counter is incremented *after* filling buffer + block -= 4; + (block << 4) + self.rng.index() as u128 + } - // update 64-bit counter - self.state[12] = self.state[12].wrapping_add(1); - if self.state[12] != 0 { return; }; - self.state[13] = self.state[13].wrapping_add(1); - } -} + /// Set the offset from the start of the stream, in 32-bit words. + /// + /// As with `get_word_pos`, we use a 68-bit number. Since the generator + /// simply cycles at the end of its period (1 ZiB), we ignore the upper + /// 60 bits. + #[inline] + pub fn set_word_pos(&mut self, word_offset: u128) { + let block = (word_offset >> 4) as u64; + self.rng + .core + .state + .set_stream_param(STREAM_PARAM_BLOCK, block); + self.rng.generate_and_set((word_offset & 15) as usize); + } -impl SeedableRng for ChaChaCore { - type Seed = [u8; SEED_WORDS*4]; - - fn from_seed(seed: Self::Seed) -> Self { - let mut seed_le = [0u32; SEED_WORDS]; - le::read_u32_into(&seed, &mut seed_le); - Self { - state: [0x61707865, 0x3320646E, 0x79622D32, 0x6B206574, // constants - seed_le[0], seed_le[1], seed_le[2], seed_le[3], // seed - seed_le[4], seed_le[5], seed_le[6], seed_le[7], // seed - 0, 0, 0, 0], // counter - } - } -} + /// Set the stream number. + /// + /// This is initialized to zero; 2<sup>64</sup> unique streams of output + /// are available per seed/key. + /// + /// Note that in order to reproduce ChaCha output with a specific 64-bit + /// nonce, one can convert that nonce to a `u64` in little-endian fashion + /// and pass to this function. In theory a 96-bit nonce can be used by + /// passing the last 64-bits to this function and using the first 32-bits as + /// the most significant half of the 64-bit counter (which may be set + /// indirectly via `set_word_pos`), but this is not directly supported. + #[inline] + pub fn set_stream(&mut self, stream: u64) { + self.rng + .core + .state + .set_stream_param(STREAM_PARAM_NONCE, stream); + if self.rng.index() != 64 { + let wp = self.get_word_pos(); + self.set_word_pos(wp); + } + } + } -impl CryptoRng for ChaChaCore {} + impl CryptoRng for $ChaChaXRng {} -impl From<ChaChaCore> for ChaChaRng { - fn from(core: ChaChaCore) -> Self { - ChaChaRng(BlockRng::new(core)) + impl From<$ChaChaXCore> for $ChaChaXRng { + fn from(core: $ChaChaXCore) -> Self { + $ChaChaXRng { + rng: BlockRng::new(core), + } + } + } } } +chacha_impl!(ChaCha20Core, ChaCha20Rng, 10, "ChaCha with 20 rounds"); +chacha_impl!(ChaCha12Core, ChaCha12Rng, 6, "ChaCha with 12 rounds"); +chacha_impl!(ChaCha8Core, ChaCha8Rng, 4, "ChaCha with 8 rounds"); + #[cfg(test)] mod test { - use ::rand_core::{RngCore, SeedableRng}; - use super::ChaChaRng; + use rand_core::{RngCore, SeedableRng}; + + type ChaChaRng = super::ChaCha20Rng; #[test] fn test_chacha_construction() { - let seed = [0,0,0,0,0,0,0,0, - 1,0,0,0,0,0,0,0, - 2,0,0,0,0,0,0,0, - 3,0,0,0,0,0,0,0]; + let seed = [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, + 0, 0, 0, + ]; let mut rng1 = ChaChaRng::from_seed(seed); assert_eq!(rng1.next_u32(), 137206642); @@ -292,18 +268,24 @@ mod test { let mut rng = ChaChaRng::from_seed(seed); let mut results = [0u32; 16]; - for i in results.iter_mut() { *i = rng.next_u32(); } - let expected = [0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, - 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b, - 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, - 0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2]; + for i in results.iter_mut() { + *i = rng.next_u32(); + } + let expected = [ + 0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8, + 0xc70d778b, 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, 0xf4b8436a, 0x1ca11815, + 0x69b687c3, 0x8665eeb2, + ]; assert_eq!(results, expected); - for i in results.iter_mut() { *i = rng.next_u32(); } - let expected = [0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, - 0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32, - 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, - 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b]; + for i in results.iter_mut() { + *i = rng.next_u32(); + } + let expected = [ + 0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612, + 0xed7aee32, 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, 0x281fed31, 0x45fb0a51, + 0x1f0ae1ac, 0x6f4d794b, + ]; assert_eq!(results, expected); } @@ -311,51 +293,62 @@ mod test { fn test_chacha_true_values_b() { // Test vector 3 from // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - let seed = [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 seed = [ + 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 mut rng = ChaChaRng::from_seed(seed); // Skip block 0 - for _ in 0..16 { rng.next_u32(); } + for _ in 0..16 { + rng.next_u32(); + } let mut results = [0u32; 16]; - for i in results.iter_mut() { *i = rng.next_u32(); } - let expected = [0x2452eb3a, 0x9249f8ec, 0x8d829d9b, 0xddd4ceb1, - 0xe8252083, 0x60818b01, 0xf38422b8, 0x5aaa49c9, - 0xbb00ca8e, 0xda3ba7b4, 0xc4b592d1, 0xfdf2732f, - 0x4436274e, 0x2561b3c8, 0xebdd4aa6, 0xa0136c00]; + for i in results.iter_mut() { + *i = rng.next_u32(); + } + let expected = [ + 0x2452eb3a, 0x9249f8ec, 0x8d829d9b, 0xddd4ceb1, 0xe8252083, 0x60818b01, 0xf38422b8, + 0x5aaa49c9, 0xbb00ca8e, 0xda3ba7b4, 0xc4b592d1, 0xfdf2732f, 0x4436274e, 0x2561b3c8, + 0xebdd4aa6, 0xa0136c00, + ]; assert_eq!(results, expected); } #[test] - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] fn test_chacha_true_values_c() { // Test vector 4 from // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - let seed = [0, 0xff, 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]; - let expected = [0xfb4dd572, 0x4bc42ef1, 0xdf922636, 0x327f1394, - 0xa78dea8f, 0x5e269039, 0xa1bebbc1, 0xcaf09aae, - 0xa25ab213, 0x48a6b46c, 0x1b9d9bcb, 0x092c5be6, - 0x546ca624, 0x1bec45d5, 0x87f47473, 0x96f0992e]; + let seed = [ + 0, 0xff, 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, + ]; + let expected = [ + 0xfb4dd572, 0x4bc42ef1, 0xdf922636, 0x327f1394, 0xa78dea8f, 0x5e269039, 0xa1bebbc1, + 0xcaf09aae, 0xa25ab213, 0x48a6b46c, 0x1b9d9bcb, 0x092c5be6, 0x546ca624, 0x1bec45d5, + 0x87f47473, 0x96f0992e, + ]; let expected_end = 3 * 16; let mut results = [0u32; 16]; // Test block 2 by skipping block 0 and 1 let mut rng1 = ChaChaRng::from_seed(seed); - for _ in 0..32 { rng1.next_u32(); } - for i in results.iter_mut() { *i = rng1.next_u32(); } + for _ in 0..32 { + rng1.next_u32(); + } + for i in results.iter_mut() { + *i = rng1.next_u32(); + } assert_eq!(results, expected); assert_eq!(rng1.get_word_pos(), expected_end); // Test block 2 by using `set_word_pos` let mut rng2 = ChaChaRng::from_seed(seed); rng2.set_word_pos(2 * 16); - for i in results.iter_mut() { *i = rng2.next_u32(); } + for i in results.iter_mut() { + *i = rng2.next_u32(); + } assert_eq!(results, expected); assert_eq!(rng2.get_word_pos(), expected_end); @@ -376,7 +369,10 @@ mod test { #[test] fn test_chacha_multiple_blocks() { - let seed = [0,0,0,0, 1,0,0,0, 2,0,0,0, 3,0,0,0, 4,0,0,0, 5,0,0,0, 6,0,0,0, 7,0,0,0]; + let seed = [ + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, + ]; let mut rng = ChaChaRng::from_seed(seed); // Store the 17*i-th 32-bit word, @@ -388,10 +384,11 @@ mod test { rng.next_u32(); } } - let expected = [0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036, - 0x49884684, 0x64efec72, 0x4be2d186, 0x3615b384, - 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530, - 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4]; + let expected = [ + 0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036, 0x49884684, 0x64efec72, 0x4be2d186, + 0x3615b384, 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530, 0x2c5bad8f, 0x898881dc, + 0x5f1c86d9, 0xc1f8e7f4, + ]; assert_eq!(results, expected); } @@ -401,10 +398,10 @@ mod test { let mut rng = ChaChaRng::from_seed(seed); let mut results = [0u8; 32]; rng.fill_bytes(&mut results); - let expected = [118, 184, 224, 173, 160, 241, 61, 144, - 64, 93, 106, 229, 83, 134, 189, 40, - 189, 210, 25, 184, 160, 141, 237, 26, - 168, 54, 239, 204, 139, 119, 13, 199]; + let expected = [ + 118, 184, 224, 173, 160, 241, 61, 144, 64, 93, 106, 229, 83, 134, 189, 40, 189, 210, + 25, 184, 160, 141, 237, 26, 168, 54, 239, 204, 139, 119, 13, 199, + ]; assert_eq!(results, expected); } @@ -420,17 +417,23 @@ mod test { rng.set_stream(2u64 << (24 + 32)); let mut results = [0u32; 16]; - for i in results.iter_mut() { *i = rng.next_u32(); } - let expected = [0x374dc6c2, 0x3736d58c, 0xb904e24a, 0xcd3f93ef, - 0x88228b1a, 0x96a4dfb3, 0x5b76ab72, 0xc727ee54, - 0x0e0e978a, 0xf3145c95, 0x1b748ea8, 0xf786c297, - 0x99c28f5f, 0x628314e8, 0x398a19fa, 0x6ded1b53]; + for i in results.iter_mut() { + *i = rng.next_u32(); + } + let expected = [ + 0x374dc6c2, 0x3736d58c, 0xb904e24a, 0xcd3f93ef, 0x88228b1a, 0x96a4dfb3, 0x5b76ab72, + 0xc727ee54, 0x0e0e978a, 0xf3145c95, 0x1b748ea8, 0xf786c297, 0x99c28f5f, 0x628314e8, + 0x398a19fa, 0x6ded1b53, + ]; assert_eq!(results, expected); } #[test] fn test_chacha_clone_streams() { - let seed = [0,0,0,0, 1,0,0,0, 2,0,0,0, 3,0,0,0, 4,0,0,0, 5,0,0,0, 6,0,0,0, 7,0,0,0]; + let seed = [ + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, + ]; let mut rng = ChaChaRng::from_seed(seed); let mut clone = rng.clone(); for _ in 0..16 { @@ -441,7 +444,7 @@ mod test { for _ in 0..7 { assert!(rng.next_u32() != clone.next_u32()); } - clone.set_stream(51); // switch part way through block + clone.set_stream(51); // switch part way through block for _ in 7..16 { assert_eq!(rng.next_u32(), clone.next_u32()); } diff --git a/rand/rand_chacha/src/lib.rs b/rand/rand_chacha/src/lib.rs index 74ad466..e374bdd 100644 --- a/rand/rand_chacha/src/lib.rs +++ b/rand/rand_chacha/src/lib.rs @@ -16,10 +16,15 @@ #![deny(missing_debug_implementations)] #![doc(test(attr(allow(unused_variables), deny(warnings))))] -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] -pub extern crate rand_core; +pub use rand_core; mod chacha; -pub use chacha::{ChaChaRng, ChaChaCore}; +pub use crate::chacha::{ChaCha12Core, ChaCha12Rng, ChaCha20Core, ChaCha20Rng, ChaCha8Core, ChaCha8Rng}; + +/// ChaCha with 20 rounds +pub type ChaChaRng = ChaCha20Rng; +/// ChaCha with 20 rounds, low-level interface +pub type ChaChaCore = ChaCha20Core; diff --git a/rand/rand_core/CHANGELOG.md b/rand/rand_core/CHANGELOG.md index 2cbb259..dfdd692 100644 --- a/rand/rand_core/CHANGELOG.md +++ b/rand/rand_core/CHANGELOG.md @@ -4,30 +4,55 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.1] - 2019-08-28 +- `OsRng` added to `rand_core` (#863) +- `Error::INTERNAL_START` and `Error::CUSTOM_START` constants (#864) +- `Error::raw_os_error` method (#864) +- `Debug` and `Display` formatting for `getrandom` error codes without `std` (#864) +### Changed +- `alloc` feature in `no_std` is available since Rust 1.36 (#856) +- Added `#[inline]` to `Error` conversion methods (#864) + +## [0.5.0] - 2019-06-06 +### Changed +- Enable testing with Miri and fix incorrect pointer usages (#779, #780, #781, #783, #784) +- Rewrite `Error` type and adjust API (#800) +- Adjust usage of `#[inline]` for `BlockRng` and `BlockRng64` + +## [0.4.0] - 2019-01-24 +### Changed +- Disable the `std` feature by default (#702) + ## [0.3.0] - 2018-09-24 +### Added - Add `SeedableRng::seed_from_u64` for convenient seeding. (#537) ## [0.2.1] - 2018-06-08 +### Added - References to a `CryptoRng` now also implement `CryptoRng`. (#470) ## [0.2.0] - 2018-05-21 +### Changed - Enable the `std` feature by default. (#409) - Remove `BlockRng{64}::inner` and `BlockRng::inner_mut`; instead making `core` public -- Add `BlockRng{64}::index` and `BlockRng{64}::generate_and_set`. (#374, #419) - Change `BlockRngCore::Results` bound to also require `AsMut<[Self::Item]>`. (#419) +### Added +- Add `BlockRng{64}::index` and `BlockRng{64}::generate_and_set`. (#374, #419) - Implement `std::io::Read` for RngCore. (#434) ## [0.1.0] - 2018-04-17 -(Split out of the Rand crate, changes here are relative to rand 0.4.2) +(Split out of the Rand crate, changes here are relative to rand 0.4.2.) +### Added - `RngCore` and `SeedableRng` are now part of `rand_core`. (#288) - Add modules to help implementing RNGs `impl` and `le`. (#209, #228) - Add `Error` and `ErrorKind`. (#225) - Add `CryptoRng` marker trait. (#273) - Add `BlockRngCore` trait. (#281) - Add `BlockRng` and `BlockRng64` wrappers to help implementations. (#281, #325) +- Add `RngCore::try_fill_bytes`. (#225) +### Changed - Revise the `SeedableRng` trait. (#233) - Remove default implementations for `RngCore::next_u64` and `RngCore::fill_bytes`. (#288) -- Add `RngCore::try_fill_bytes`. (#225) ## [0.0.1] - 2017-09-14 (yanked) Experimental version as part of the rand crate refactor. diff --git a/rand/rand_core/Cargo.toml b/rand/rand_core/Cargo.toml index 1678773..e52af5f 100644 --- a/rand/rand_core/Cargo.toml +++ b/rand/rand_core/Cargo.toml @@ -1,28 +1,28 @@ [package] name = "rand_core" -version = "0.3.0" +version = "0.5.1" authors = ["The Rand Project Developers", "The Rust Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" -documentation = "https://rust-random.github.io/rand/rand_core" +documentation = "https://rust-random.github.io/rand/rand_core/" homepage = "https://crates.io/crates/rand_core" description = """ Core random number generator traits and tools for implementation. """ keywords = ["random", "rng"] categories = ["algorithms", "no-std"] +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } [features] -default = ["std"] -std = ["alloc"] # use std library; should be default but for above bug +std = ["alloc", "getrandom", "getrandom/std"] # use std library; should be default but for above bug alloc = [] # enables Vec and Box support without std -serde1 = ["serde", "serde_derive"] # enables serde for BlockRng wrapper +serde1 = ["serde"] # enables serde for BlockRng wrapper [dependencies] -serde = { version = "1", optional = true } -serde_derive = { version = "^1.0.38", optional = true } +serde = { version = "1", features = ["derive"], optional = true } +getrandom = { version = "0.1", optional = true } diff --git a/rand/rand_core/README.md b/rand/rand_core/README.md index dee6504..467e66f 100644 --- a/rand/rand_core/README.md +++ b/rand/rand_core/README.md @@ -6,7 +6,7 @@ [![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_core) [![API](https://docs.rs/rand_core/badge.svg)](https://docs.rs/rand_core) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) Core traits and error types of the [rand] library, plus tools for implementing RNGs. @@ -25,7 +25,7 @@ Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_core) - [API documentation (docs.rs)](https://docs.rs/rand_core) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_core/CHANGELOG.md) [rand]: https://crates.io/crates/rand @@ -40,17 +40,34 @@ The `rand_core` crate provides: The traits and error types are also available via `rand`. +## Versions + +The current version is: +``` +rand_core = "0.5.0" +``` + +Rand libs have inter-dependencies and make use of the +[semver trick](https://github.com/dtolnay/semver-trick/) in order to make traits +compatible across crate versions. (This is especially important for `RngCore` +and `SeedableRng`.) A few crate releases are thus compatibility shims, +depending on the *next* lib version (e.g. `rand_core` versions `0.2.2` and +`0.3.1`). This means, for example, that `rand_core_0_4_0::SeedableRng` and +`rand_core_0_3_0::SeedableRng` are distinct, incompatible traits, which can +cause build errors. Usually, running `cargo update` is enough to fix any issues. + ## Crate Features `rand_core` supports `no_std` and `alloc`-only configurations, as well as full `std` functionality. The differences between `no_std` and full `std` are small, -comprising `RngCore` support for `Box<R>` types where `R: RngCore`, as well as +comprising `RngCore` support for `Box<R>` types where `R: RngCore`, +`std::io::Read` support for types supporting `RngCore`, and extensions to the `Error` type's functionality. -Due to [rust-lang/cargo#1596](https://github.com/rust-lang/cargo/issues/1596), -`rand_core` is built without `std` support by default. Since features are -unioned across the whole dependency tree, any crate using `rand` with its -default features will also enable `std` support in `rand_core`. +The `std` feature is *not enabled by default*. This is primarily to avoid build +problems where one crate implicitly requires `rand_core` with `std` support and +another crate requires `rand` *without* `std` support. However, the `rand` crate +continues to enable `std` support by default, both for itself and `rand_core`. The `serde1` feature can be used to derive `Serialize` and `Deserialize` for RNG implementations that use the `BlockRng` or `BlockRng64` wrappers. diff --git a/rand/rand_core/src/block.rs b/rand/rand_core/src/block.rs index de480e4..0ab7458 100644 --- a/rand/rand_core/src/block.rs +++ b/rand/rand_core/src/block.rs @@ -16,55 +16,55 @@ //! implementations only need to concern themselves with generation of the //! block, not the various [`RngCore`] methods (especially [`fill_bytes`], where //! the optimal implementations are not trivial), and this allows -//! [`ReseedingRng`] perform periodic reseeding with very low overhead. +//! `ReseedingRng` (see [`rand`](https://docs.rs/rand) crate) perform periodic +//! reseeding with very low overhead. //! //! # Example -//! +//! //! ```norun //! use rand_core::block::{BlockRngCore, BlockRng}; -//! +//! //! struct MyRngCore; -//! +//! //! impl BlockRngCore for MyRngCore { //! type Results = [u32; 16]; -//! +//! //! fn generate(&mut self, results: &mut Self::Results) { //! unimplemented!() //! } //! } -//! +//! //! impl SeedableRng for MyRngCore { //! type Seed = unimplemented!(); //! fn from_seed(seed: Self::Seed) -> Self { //! unimplemented!() //! } //! } -//! +//! //! // optionally, also implement CryptoRng for MyRngCore -//! +//! //! // Final RNG. //! type MyRng = BlockRng<u32, MyRngCore>; //! ``` -//! -//! [`BlockRngCore`]: trait.BlockRngCore.html -//! [`RngCore`]: ../trait.RngCore.html -//! [`fill_bytes`]: ../trait.RngCore.html#tymethod.fill_bytes -//! [`ReseedingRng`]: ../../rand/rngs/adapter/struct.ReseedingRng.html +//! +//! [`BlockRngCore`]: crate::block::BlockRngCore +//! [`fill_bytes`]: RngCore::fill_bytes use core::convert::AsRef; -use core::fmt; -use {RngCore, CryptoRng, SeedableRng, Error}; -use impls::{fill_via_u32_chunks, fill_via_u64_chunks}; +use core::{fmt, ptr}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; +use crate::{RngCore, CryptoRng, SeedableRng, Error}; +use crate::impls::{fill_via_u32_chunks, fill_via_u64_chunks}; /// A trait for RNGs which do not generate random numbers individually, but in /// blocks (typically `[u32; N]`). This technique is commonly used by /// cryptographic RNGs to improve performance. -/// -/// See the [module documentation](index.html) for details. +/// +/// See the [module][crate::block] documentation for details. pub trait BlockRngCore { /// Results element type, e.g. `u32`. type Item; - + /// Results type. This is the 'block' an RNG implementing `BlockRngCore` /// generates, which will usually be an array like `[u32; 16]`. type Results: AsRef<[Self::Item]> + AsMut<[Self::Item]> + Default; @@ -105,15 +105,10 @@ pub trait BlockRngCore { /// /// For easy initialization `BlockRng` also implements [`SeedableRng`]. /// -/// [`BlockRngCore`]: BlockRngCore.t.html -/// [`BlockRngCore::generate`]: trait.BlockRngCore.html#tymethod.generate -/// [`BlockRng64`]: struct.BlockRng64.html -/// [`RngCore`]: ../RngCore.t.html -/// [`next_u32`]: ../trait.RngCore.html#tymethod.next_u32 -/// [`next_u64`]: ../trait.RngCore.html#tymethod.next_u64 -/// [`fill_bytes`]: ../trait.RngCore.html#tymethod.fill_bytes -/// [`try_fill_bytes`]: ../trait.RngCore.html#tymethod.try_fill_bytes -/// [`SeedableRng`]: ../SeedableRng.t.html +/// [`next_u32`]: RngCore::next_u32 +/// [`next_u64`]: RngCore::next_u64 +/// [`fill_bytes`]: RngCore::fill_bytes +/// [`try_fill_bytes`]: RngCore::try_fill_bytes #[derive(Clone)] #[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct BlockRng<R: BlockRngCore + ?Sized> { @@ -137,6 +132,7 @@ impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng<R> { impl<R: BlockRngCore> BlockRng<R> { /// Create a new `BlockRng` from an existing RNG implementing /// `BlockRngCore`. Results will be generated on first use. + #[inline] pub fn new(core: R) -> BlockRng<R>{ let results_empty = R::Results::default(); BlockRng { @@ -147,22 +143,25 @@ impl<R: BlockRngCore> BlockRng<R> { } /// Get the index into the result buffer. - /// + /// /// If this is equal to or larger than the size of the result buffer then /// the buffer is "empty" and `generate()` must be called to produce new /// results. + #[inline(always)] pub fn index(&self) -> usize { self.index } /// Reset the number of available results. /// This will force a new set of results to be generated on next use. + #[inline] pub fn reset(&mut self) { self.index = self.results.as_ref().len(); } /// Generate a new set of results immediately, setting the index to the /// given value. + #[inline] pub fn generate_and_set(&mut self, index: usize) { assert!(index < self.results.as_ref().len()); self.core.generate(&mut self.results); @@ -173,7 +172,7 @@ impl<R: BlockRngCore> BlockRng<R> { impl<R: BlockRngCore<Item=u32>> RngCore for BlockRng<R> where <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]> { - #[inline(always)] + #[inline] fn next_u32(&mut self) -> u32 { if self.index >= self.results.as_ref().len() { self.generate_and_set(0); @@ -184,12 +183,14 @@ where <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]> value } - #[inline(always)] + #[inline] fn next_u64(&mut self) -> u64 { let read_u64 = |results: &[u32], index| { - if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { - // requires little-endian CPU supporting unaligned reads: - unsafe { *(&results[index] as *const u32 as *const u64) } + if cfg!(any(target_endian = "little")) { + // requires little-endian CPU + #[allow(clippy::cast_ptr_alignment)] // false positive + let ptr: *const u64 = results[index..=index+1].as_ptr() as *const u64; + unsafe { ptr::read_unaligned(ptr) } } else { let x = u64::from(results[index]); let y = u64::from(results[index + 1]); @@ -215,48 +216,7 @@ where <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]> } } - // As an optimization we try to write directly into the output buffer. - // This is only enabled for little-endian platforms where unaligned writes - // are known to be safe and fast. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn fill_bytes(&mut self, dest: &mut [u8]) { - let mut filled = 0; - - // Continue filling from the current set of results - if self.index < self.results.as_ref().len() { - let (consumed_u32, filled_u8) = - fill_via_u32_chunks(&self.results.as_ref()[self.index..], - dest); - - self.index += consumed_u32; - filled += filled_u8; - } - - let len_remainder = - (dest.len() - filled) % (self.results.as_ref().len() * 4); - let end_direct = dest.len() - len_remainder; - - while filled < end_direct { - let dest_u32: &mut R::Results = unsafe { - &mut *(dest[filled..].as_mut_ptr() as - *mut <R as BlockRngCore>::Results) - }; - self.core.generate(dest_u32); - filled += self.results.as_ref().len() * 4; - self.index = self.results.as_ref().len(); - } - - if len_remainder > 0 { - self.core.generate(&mut self.results); - let (consumed_u32, _) = - fill_via_u32_chunks(self.results.as_ref(), - &mut dest[filled..]); - - self.index = consumed_u32; - } - } - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { let mut read_len = 0; while read_len < dest.len() { @@ -272,6 +232,7 @@ where <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]> } } + #[inline(always)] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.fill_bytes(dest); Ok(()) @@ -281,14 +242,17 @@ where <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]> impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng<R> { type Seed = R::Seed; + #[inline(always)] fn from_seed(seed: Self::Seed) -> Self { Self::new(R::from_seed(seed)) } + #[inline(always)] fn seed_from_u64(seed: u64) -> Self { Self::new(R::seed_from_u64(seed)) } + #[inline(always)] fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> { Ok(Self::new(R::from_rng(rng)?)) } @@ -314,13 +278,10 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng<R> { /// values. If the requested length is not a multiple of 8, some bytes will be /// discarded. /// -/// [`BlockRngCore`]: BlockRngCore.t.html -/// [`RngCore`]: ../RngCore.t.html -/// [`next_u32`]: ../trait.RngCore.html#tymethod.next_u32 -/// [`next_u64`]: ../trait.RngCore.html#tymethod.next_u64 -/// [`fill_bytes`]: ../trait.RngCore.html#tymethod.fill_bytes -/// [`try_fill_bytes`]: ../trait.RngCore.html#tymethod.try_fill_bytes -/// [`BlockRng`]: struct.BlockRng.html +/// [`next_u32`]: RngCore::next_u32 +/// [`next_u64`]: RngCore::next_u64 +/// [`fill_bytes`]: RngCore::fill_bytes +/// [`try_fill_bytes`]: RngCore::try_fill_bytes #[derive(Clone)] #[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct BlockRng64<R: BlockRngCore + ?Sized> { @@ -346,6 +307,7 @@ impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng64<R> { impl<R: BlockRngCore> BlockRng64<R> { /// Create a new `BlockRng` from an existing RNG implementing /// `BlockRngCore`. Results will be generated on first use. + #[inline] pub fn new(core: R) -> BlockRng64<R>{ let results_empty = R::Results::default(); BlockRng64 { @@ -361,12 +323,14 @@ impl<R: BlockRngCore> BlockRng64<R> { /// If this is equal to or larger than the size of the result buffer then /// the buffer is "empty" and `generate()` must be called to produce new /// results. + #[inline(always)] pub fn index(&self) -> usize { self.index } /// Reset the number of available results. /// This will force a new set of results to be generated on next use. + #[inline] pub fn reset(&mut self) { self.index = self.results.as_ref().len(); self.half_used = false; @@ -374,6 +338,7 @@ impl<R: BlockRngCore> BlockRng64<R> { /// Generate a new set of results immediately, setting the index to the /// given value. + #[inline] pub fn generate_and_set(&mut self, index: usize) { assert!(index < self.results.as_ref().len()); self.core.generate(&mut self.results); @@ -385,7 +350,7 @@ impl<R: BlockRngCore> BlockRng64<R> { impl<R: BlockRngCore<Item=u64>> RngCore for BlockRng64<R> where <R as BlockRngCore>::Results: AsRef<[u64]> + AsMut<[u64]> { - #[inline(always)] + #[inline] fn next_u32(&mut self) -> u32 { let mut index = self.index * 2 - self.half_used as usize; if index >= self.results.as_ref().len() * 2 { @@ -411,7 +376,7 @@ where <R as BlockRngCore>::Results: AsRef<[u64]> + AsMut<[u64]> } } - #[inline(always)] + #[inline] fn next_u64(&mut self) -> u64 { if self.index >= self.results.as_ref().len() { self.core.generate(&mut self.results); @@ -424,48 +389,7 @@ where <R as BlockRngCore>::Results: AsRef<[u64]> + AsMut<[u64]> value } - // As an optimization we try to write directly into the output buffer. - // This is only enabled for little-endian platforms where unaligned writes - // are known to be safe and fast. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn fill_bytes(&mut self, dest: &mut [u8]) { - let mut filled = 0; - self.half_used = false; - - // Continue filling from the current set of results - if self.index < self.results.as_ref().len() { - let (consumed_u64, filled_u8) = - fill_via_u64_chunks(&self.results.as_ref()[self.index..], - dest); - - self.index += consumed_u64; - filled += filled_u8; - } - - let len_remainder = - (dest.len() - filled) % (self.results.as_ref().len() * 8); - let end_direct = dest.len() - len_remainder; - - while filled < end_direct { - let dest_u64: &mut R::Results = unsafe { - ::core::mem::transmute(dest[filled..].as_mut_ptr()) - }; - self.core.generate(dest_u64); - filled += self.results.as_ref().len() * 8; - self.index = self.results.as_ref().len(); - } - - if len_remainder > 0 { - self.core.generate(&mut self.results); - let (consumed_u64, _) = - fill_via_u64_chunks(&mut self.results.as_ref(), - &mut dest[filled..]); - - self.index = consumed_u64; - } - } - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { let mut read_len = 0; self.half_used = false; @@ -484,22 +408,27 @@ where <R as BlockRngCore>::Results: AsRef<[u64]> + AsMut<[u64]> } } + #[inline(always)] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + self.fill_bytes(dest); + Ok(()) } } impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> { type Seed = R::Seed; + #[inline(always)] fn from_seed(seed: Self::Seed) -> Self { Self::new(R::from_seed(seed)) } + #[inline(always)] fn seed_from_u64(seed: u64) -> Self { Self::new(R::seed_from_u64(seed)) } + #[inline(always)] fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> { Ok(Self::new(R::from_rng(rng)?)) } diff --git a/rand/rand_core/src/error.rs b/rand/rand_core/src/error.rs index 5a8459e..30b095c 100644 --- a/rand/rand_core/src/error.rs +++ b/rand/rand_core/src/error.rs @@ -9,169 +9,182 @@ //! Error types use core::fmt; +use core::num::NonZeroU32; -#[cfg(feature="std")] -use std::error::Error as stdError; -#[cfg(feature="std")] -use std::io; -/// Error kind which can be matched over. -#[derive(PartialEq, Eq, Debug, Copy, Clone)] -pub enum ErrorKind { - /// Feature is not available; not recoverable. - /// - /// This is the most permanent failure type and implies the error cannot be - /// resolved simply by retrying (e.g. the feature may not exist in this - /// build of the application or on the current platform). - Unavailable, - /// General failure; there may be a chance of recovery on retry. - /// - /// This is the catch-all kind for errors from known and unknown sources - /// which do not have a more specific kind / handling method. - /// - /// It is suggested to retry a couple of times or retry later when - /// handling; some error sources may be able to resolve themselves, - /// although this is not likely. - Unexpected, - /// A transient failure which likely can be resolved or worked around. - /// - /// This error kind exists for a few specific cases where it is known that - /// the error likely can be resolved internally, but is reported anyway. - Transient, - /// Not ready yet: recommended to try again a little later. - /// - /// This error kind implies the generator needs more time or needs some - /// other part of the application to do something else first before it is - /// ready for use; for example this may be used by external generators - /// which require time for initialization. - NotReady, - #[doc(hidden)] - __Nonexhaustive, +/// Error type of random number generators +/// +/// In order to be compatible with `std` and `no_std`, this type has two +/// possible implementations: with `std` a boxed `Error` trait object is stored, +/// while with `no_std` we merely store an error code. +pub struct Error { + #[cfg(feature="std")] + inner: Box<dyn std::error::Error + Send + Sync + 'static>, + #[cfg(not(feature="std"))] + code: NonZeroU32, } -impl ErrorKind { - /// True if this kind of error may resolve itself on retry. +impl Error { + /// Construct from any type supporting `std::error::Error` + /// + /// Available only when configured with `std`. /// - /// See also `should_wait()`. - pub fn should_retry(self) -> bool { - self != ErrorKind::Unavailable + /// See also `From<NonZeroU32>`, which is available with and without `std`. + #[cfg(feature="std")] + #[inline] + pub fn new<E>(err: E) -> Self + where E: Into<Box<dyn std::error::Error + Send + Sync + 'static>> + { + Error { inner: err.into() } } - /// True if we should retry but wait before retrying + /// Reference the inner error (`std` only) /// - /// This implies `should_retry()` is true. - pub fn should_wait(self) -> bool { - self == ErrorKind::NotReady + /// When configured with `std`, this is a trivial operation and never + /// panics. Without `std`, this method is simply unavailable. + #[cfg(feature="std")] + #[inline] + pub fn inner(&self) -> &(dyn std::error::Error + Send + Sync + 'static) { + &*self.inner } - /// A description of this error kind - pub fn description(self) -> &'static str { - match self { - ErrorKind::Unavailable => "permanently unavailable", - ErrorKind::Unexpected => "unexpected failure", - ErrorKind::Transient => "transient failure", - ErrorKind::NotReady => "not ready yet", - ErrorKind::__Nonexhaustive => unreachable!(), - } + /// Unwrap the inner error (`std` only) + /// + /// When configured with `std`, this is a trivial operation and never + /// panics. Without `std`, this method is simply unavailable. + #[cfg(feature="std")] + #[inline] + pub fn take_inner(self) -> Box<dyn std::error::Error + Send + Sync + 'static> { + self.inner } -} - + + /// Codes below this point represent OS Errors (i.e. positive i32 values). + /// Codes at or above this point, but below [`Error::CUSTOM_START`] are + /// reserved for use by the `rand` and `getrandom` crates. + pub const INTERNAL_START: u32 = 1 << 31; -/// Error type of random number generators -/// -/// This is a relatively simple error type, designed for compatibility with and -/// without the Rust `std` library. It embeds a "kind" code, a message (static -/// string only), and an optional chained cause (`std` only). The `kind` and -/// `msg` fields can be accessed directly; cause can be accessed via -/// `std::error::Error::cause` or `Error::take_cause`. Construction can only be -/// done via `Error::new` or `Error::with_cause`. -#[derive(Debug)] -pub struct Error { - /// The error kind - pub kind: ErrorKind, - /// The error message - pub msg: &'static str, - #[cfg(feature="std")] - cause: Option<Box<stdError + Send + Sync>>, -} + /// Codes at or above this point can be used by users to define their own + /// custom errors. + pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); -impl Error { - /// Create a new instance, with specified kind and a message. - pub fn new(kind: ErrorKind, msg: &'static str) -> Self { + /// Extract the raw OS error code (if this error came from the OS) + /// + /// This method is identical to `std::io::Error::raw_os_error()`, except + /// that it works in `no_std` contexts. If this method returns `None`, the + /// error value can still be formatted via the `Diplay` implementation. + #[inline] + pub fn raw_os_error(&self) -> Option<i32> { #[cfg(feature="std")] { - Error { kind, msg, cause: None } + if let Some(e) = self.inner.downcast_ref::<std::io::Error>() { + return e.raw_os_error(); + } } - #[cfg(not(feature="std"))] { - Error { kind, msg } + match self.code() { + Some(code) if u32::from(code) < Self::INTERNAL_START => + Some(u32::from(code) as i32), + _ => None, } } - - /// Create a new instance, with specified kind, message, and a - /// chained cause. - /// - /// Note: `stdError` is an alias for `std::error::Error`. - /// - /// If not targetting `std` (i.e. `no_std`), this function is replaced by - /// another with the same prototype, except that there are no bounds on the - /// type `E` (because both `Box` and `stdError` are unavailable), and the - /// `cause` is ignored. - #[cfg(feature="std")] - pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, cause: E) -> Self - where E: Into<Box<stdError + Send + Sync>> - { - Error { kind, msg, cause: Some(cause.into()) } - } - - /// Create a new instance, with specified kind, message, and a - /// chained cause. + + /// Retrieve the error code, if any. /// - /// In `no_std` mode the *cause* is ignored. - #[cfg(not(feature="std"))] - pub fn with_cause<E>(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { - Error { kind, msg } + /// If this `Error` was constructed via `From<NonZeroU32>`, then this method + /// will return this `NonZeroU32` code (for `no_std` this is always the + /// case). Otherwise, this method will return `None`. + #[inline] + pub fn code(&self) -> Option<NonZeroU32> { + #[cfg(feature="std")] { + self.inner.downcast_ref::<ErrorCode>().map(|c| c.0) + } + #[cfg(not(feature="std"))] { + Some(self.code) + } } - - /// Take the cause, if any. This allows the embedded cause to be extracted. - /// This uses `Option::take`, leaving `self` with no cause. - #[cfg(feature="std")] - pub fn take_cause(&mut self) -> Option<Box<stdError + Send + Sync>> { - self.cause.take() +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(feature="std")] { + write!(f, "Error {{ inner: {:?} }}", self.inner) + } + #[cfg(all(feature="getrandom", not(feature="std")))] { + getrandom::Error::from(self.code).fmt(f) + } + #[cfg(not(feature="getrandom"))] { + write!(f, "Error {{ code: {} }}", self.code) + } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[cfg(feature="std")] { - if let Some(ref cause) = self.cause { - return write!(f, "{} ({}); cause: {}", - self.msg, self.kind.description(), cause); - } + write!(f, "{}", self.inner) + } + #[cfg(all(feature="getrandom", not(feature="std")))] { + getrandom::Error::from(self.code).fmt(f) + } + #[cfg(not(feature="getrandom"))] { + write!(f, "error code {}", self.code) } - write!(f, "{} ({})", self.msg, self.kind.description()) } } -#[cfg(feature="std")] -impl stdError for Error { - fn description(&self) -> &str { - self.msg +impl From<NonZeroU32> for Error { + #[inline] + fn from(code: NonZeroU32) -> Self { + #[cfg(feature="std")] { + Error { inner: Box::new(ErrorCode(code)) } + } + #[cfg(not(feature="std"))] { + Error { code } + } + } +} + +#[cfg(feature="getrandom")] +impl From<getrandom::Error> for Error { + #[inline] + fn from(error: getrandom::Error) -> Self { + #[cfg(feature="std")] { + Error { inner: Box::new(error) } + } + #[cfg(not(feature="std"))] { + Error { code: error.code() } + } } +} - fn cause(&self) -> Option<&stdError> { - self.cause.as_ref().map(|e| e.as_ref() as &stdError) +#[cfg(feature="std")] +impl std::error::Error for Error { + #[inline] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.source() } } #[cfg(feature="std")] -impl From<Error> for io::Error { +impl From<Error> for std::io::Error { + #[inline] fn from(error: Error) -> Self { - use std::io::ErrorKind::*; - match error.kind { - ErrorKind::Unavailable => io::Error::new(NotFound, error), - ErrorKind::Unexpected | - ErrorKind::Transient => io::Error::new(Other, error), - ErrorKind::NotReady => io::Error::new(WouldBlock, error), - ErrorKind::__Nonexhaustive => unreachable!(), + if let Some(code) = error.raw_os_error() { + std::io::Error::from_raw_os_error(code) + } else { + std::io::Error::new(std::io::ErrorKind::Other, error) } } } + +#[cfg(feature="std")] +#[derive(Debug, Copy, Clone)] +struct ErrorCode(NonZeroU32); + +#[cfg(feature="std")] +impl fmt::Display for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "error code {}", self.0) + } +} + +#[cfg(feature="std")] +impl std::error::Error for ErrorCode {} diff --git a/rand/rand_core/src/impls.rs b/rand/rand_core/src/impls.rs index 57bdd07..dee4ed1 100644 --- a/rand/rand_core/src/impls.rs +++ b/rand/rand_core/src/impls.rs @@ -17,12 +17,11 @@ //! to/from byte sequences, and since its purpose is reproducibility, //! non-reproducible sources (e.g. `OsRng`) need not bother with it. -use core::intrinsics::transmute; use core::ptr::copy_nonoverlapping; use core::slice; use core::cmp::min; use core::mem::size_of; -use RngCore; +use crate::RngCore; /// Implement `next_u64` via `next_u32`, little-endian order. @@ -44,21 +43,15 @@ pub fn fill_bytes_via_next<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) { while left.len() >= 8 { let (l, r) = {left}.split_at_mut(8); left = r; - let chunk: [u8; 8] = unsafe { - transmute(rng.next_u64().to_le()) - }; + let chunk: [u8; 8] = rng.next_u64().to_le_bytes(); l.copy_from_slice(&chunk); } let n = left.len(); if n > 4 { - let chunk: [u8; 8] = unsafe { - transmute(rng.next_u64().to_le()) - }; + let chunk: [u8; 8] = rng.next_u64().to_le_bytes(); left.copy_from_slice(&chunk[..n]); } else if n > 0 { - let chunk: [u8; 4] = unsafe { - transmute(rng.next_u32().to_le()) - }; + let chunk: [u8; 4] = rng.next_u32().to_le_bytes(); left.copy_from_slice(&chunk[..n]); } } diff --git a/rand/rand_core/src/lib.rs b/rand/rand_core/src/lib.rs index a65db93..d8e0189 100644 --- a/rand/rand_core/src/lib.rs +++ b/rand/rand_core/src/lib.rs @@ -8,29 +8,24 @@ // except according to those terms. //! Random number generation traits -//! +//! //! This crate is mainly of interest to crates publishing implementations of -//! [`RngCore`]. Other users are encouraged to use the [rand] crate instead +//! [`RngCore`]. Other users are encouraged to use the [`rand`] crate instead //! which re-exports the main traits and error types. //! //! [`RngCore`] is the core trait implemented by algorithmic pseudo-random number //! generators and external random-number sources. -//! +//! //! [`SeedableRng`] is an extension trait for construction from fixed seeds and //! other random number generators. -//! +//! //! [`Error`] is provided for error-handling. It is safe to use in `no_std` //! environments. -//! +//! //! The [`impls`] and [`le`] sub-modules include a few small functions to assist //! implementation of [`RngCore`]. -//! -//! [rand]: https://crates.io/crates/rand -//! [`RngCore`]: trait.RngCore.html -//! [`SeedableRng`]: trait.SeedableRng.html -//! [`Error`]: struct.Error.html -//! [`impls`]: impls/index.html -//! [`le`]: le/index.html +//! +//! [`rand`]: https://docs.rs/rand #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", @@ -40,59 +35,58 @@ #![deny(missing_debug_implementations)] #![doc(test(attr(allow(unused_variables), deny(warnings))))] -#![cfg_attr(not(feature="std"), no_std)] -#![cfg_attr(all(feature="alloc", not(feature="std")), feature(alloc))] +#![allow(clippy::unreadable_literal)] -#[cfg(feature="std")] extern crate core; -#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; -#[cfg(feature="serde1")] extern crate serde; -#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive; +#![cfg_attr(not(feature="std"), no_std)] use core::default::Default; use core::convert::AsMut; use core::ptr::copy_nonoverlapping; +#[cfg(all(feature="alloc", not(feature="std")))] extern crate alloc; #[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box; -pub use error::{ErrorKind, Error}; +pub use error::Error; +#[cfg(feature="getrandom")] pub use os::OsRng; mod error; pub mod block; pub mod impls; pub mod le; +#[cfg(feature="getrandom")] mod os; /// The core of a random number generator. -/// +/// /// This trait encapsulates the low-level functionality common to all /// generators, and is the "back end", to be implemented by generators. -/// End users should normally use [`Rng`] from the [rand] crate, which is -/// automatically implemented for every type implementing `RngCore`. -/// +/// End users should normally use the `Rng` trait from the [`rand`] crate, +/// which is automatically implemented for every type implementing `RngCore`. +/// /// Three different methods for generating random data are provided since the /// optimal implementation of each is dependent on the type of generator. There /// is no required relationship between the output of each; e.g. many /// implementations of [`fill_bytes`] consume a whole number of `u32` or `u64` /// values and drop any remaining unused bytes. -/// +/// /// The [`try_fill_bytes`] method is a variant of [`fill_bytes`] allowing error /// handling; it is not deemed sufficiently useful to add equivalents for /// [`next_u32`] or [`next_u64`] since the latter methods are almost always used /// with algorithmic generators (PRNGs), which are normally infallible. -/// +/// /// Algorithmic generators implementing [`SeedableRng`] should normally have /// *portable, reproducible* output, i.e. fix Endianness when converting values /// to avoid platform differences, and avoid making any changes which affect /// output (except by communicating that the release has breaking changes). -/// +/// /// Typically implementators will implement only one of the methods available /// in this trait directly, then use the helper functions from the -/// [`rand_core::impls`] module to implement the other methods. -/// +/// [`impls`] module to implement the other methods. +/// /// It is recommended that implementations also implement: -/// +/// /// - `Debug` with a custom implementation which *does not* print any internal /// state (at least, [`CryptoRng`]s should not risk leaking state through /// `Debug`). @@ -104,72 +98,69 @@ pub mod le; /// implement [`SeedableRng`], to guide users towards proper seeding. /// External / hardware RNGs can choose to implement `Default`. /// - `Eq` and `PartialEq` could be implemented, but are probably not useful. -/// +/// /// # Example -/// +/// /// A simple example, obviously not generating very *random* output: -/// +/// /// ``` /// #![allow(dead_code)] /// use rand_core::{RngCore, Error, impls}; -/// +/// /// struct CountingRng(u64); -/// +/// /// impl RngCore for CountingRng { /// fn next_u32(&mut self) -> u32 { /// self.next_u64() as u32 /// } -/// +/// /// fn next_u64(&mut self) -> u64 { /// self.0 += 1; /// self.0 /// } -/// +/// /// fn fill_bytes(&mut self, dest: &mut [u8]) { /// impls::fill_bytes_via_next(self, dest) /// } -/// +/// /// fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { /// Ok(self.fill_bytes(dest)) /// } /// } /// ``` -/// -/// [rand]: https://crates.io/crates/rand -/// [`Rng`]: ../rand/trait.Rng.html -/// [`SeedableRng`]: trait.SeedableRng.html -/// [`rand_core::impls`]: ../rand_core/impls/index.html -/// [`try_fill_bytes`]: trait.RngCore.html#tymethod.try_fill_bytes -/// [`fill_bytes`]: trait.RngCore.html#tymethod.fill_bytes -/// [`next_u32`]: trait.RngCore.html#tymethod.next_u32 -/// [`next_u64`]: trait.RngCore.html#tymethod.next_u64 -/// [`CryptoRng`]: trait.CryptoRng.html +/// +/// [`rand`]: https://docs.rs/rand +/// [`try_fill_bytes`]: RngCore::try_fill_bytes +/// [`fill_bytes`]: RngCore::fill_bytes +/// [`next_u32`]: RngCore::next_u32 +/// [`next_u64`]: RngCore::next_u64 pub trait RngCore { /// Return the next random `u32`. /// /// RNGs must implement at least one method from this trait directly. In /// the case this method is not implemented directly, it can be implemented - /// using `self.next_u64() as u32` or - /// [via `fill_bytes`](../rand_core/impls/fn.next_u32_via_fill.html). + /// using `self.next_u64() as u32` or via + /// [`fill_bytes`](impls::next_u32_via_fill). fn next_u32(&mut self) -> u32; /// Return the next random `u64`. /// /// RNGs must implement at least one method from this trait directly. In /// the case this method is not implemented directly, it can be implemented - /// [via `next_u32`](../rand_core/impls/fn.next_u64_via_u32.html) or - /// [via `fill_bytes`](../rand_core/impls/fn.next_u64_via_fill.html). + /// via [`next_u32`](impls::next_u64_via_u32) or via + /// [`fill_bytes`](impls::next_u64_via_fill). fn next_u64(&mut self) -> u64; /// Fill `dest` with random data. /// /// RNGs must implement at least one method from this trait directly. In /// the case this method is not implemented directly, it can be implemented - /// [via `next_u*`](../rand_core/impls/fn.fill_bytes_via_next.html) or - /// via `try_fill_bytes`; if this generator can fail the implementation - /// must choose how best to handle errors here (e.g. panic with a - /// descriptive message or log a warning and retry a few times). - /// + /// via [`next_u*`](impls::fill_bytes_via_next) or + /// via [`try_fill_bytes`](RngCore::try_fill_bytes); if this generator can + /// fail the implementation must choose how best to handle errors here + /// (e.g. panic with a descriptive message or log a warning and retry a few + /// times). + /// /// This method should guarantee that `dest` is entirely filled /// with new data, and may panic if this is impossible /// (e.g. reading past the end of a file that is being used as the @@ -182,51 +173,46 @@ pub trait RngCore { /// generating random data thus making this the primary method implemented /// by external (true) RNGs (e.g. `OsRng`) which can fail. It may be used /// directly to generate keys and to seed (infallible) PRNGs. - /// + /// /// Other than error handling, this method is identical to [`fill_bytes`]; /// thus this may be implemented using `Ok(self.fill_bytes(dest))` or /// `fill_bytes` may be implemented with /// `self.try_fill_bytes(dest).unwrap()` or more specific error handling. - /// - /// [`fill_bytes`]: trait.RngCore.html#method.fill_bytes + /// + /// [`fill_bytes`]: RngCore::fill_bytes fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error>; } /// A marker trait used to indicate that an [`RngCore`] or [`BlockRngCore`] /// implementation is supposed to be cryptographically secure. -/// +/// /// *Cryptographically secure generators*, also known as *CSPRNGs*, should /// satisfy an additional properties over other generators: given the first /// *k* bits of an algorithm's output /// sequence, it should not be possible using polynomial-time algorithms to /// predict the next bit with probability significantly greater than 50%. -/// +/// /// Some generators may satisfy an additional property, however this is not /// required by this trait: if the CSPRNG's state is revealed, it should not be /// computationally-feasible to reconstruct output prior to this. Some other /// generators allow backwards-computation and are consided *reversible*. -/// +/// /// Note that this trait is provided for guidance only and cannot guarantee /// suitability for cryptographic applications. In general it should only be /// implemented for well-reviewed code implementing well-regarded algorithms. -/// +/// /// Note also that use of a `CryptoRng` does not protect against other /// weaknesses such as seeding from a weak entropy source or leaking state. -/// -/// [`RngCore`]: trait.RngCore.html -/// [`BlockRngCore`]: ../rand_core/block/trait.BlockRngCore.html +/// +/// [`BlockRngCore`]: block::BlockRngCore pub trait CryptoRng {} /// A random number generator that can be explicitly seeded. /// /// This trait encapsulates the low-level functionality common to all /// pseudo-random number generators (PRNGs, or algorithmic generators). -/// -/// The [`rand::FromEntropy`] trait is automatically implemented for every type -/// implementing `SeedableRng`, providing a convenient `from_entropy()` -/// constructor. -/// -/// [`rand::FromEntropy`]: ../rand/trait.FromEntropy.html +/// +/// [`rand`]: https://docs.rs/rand pub trait SeedableRng: Sized { /// Seed type, which is restricted to types mutably-dereferencable as `u8` /// arrays (we recommend `[u8; N]` for some `N`). @@ -279,14 +265,18 @@ pub trait SeedableRng: Sized { /// /// PRNG implementations are allowed to assume that bits in the seed are /// well distributed. That means usually that the number of one and zero - /// bits are about equal, and values like 0, 1 and (size - 1) are unlikely. + /// bits are roughly equal, and values like 0, 1 and (size - 1) are unlikely. + /// Note that many non-cryptographic PRNGs will show poor quality output + /// if this is not adhered to. If you wish to seed from simple numbers, use + /// `seed_from_u64` instead. /// - /// PRNG implementations are recommended to be reproducible. A PRNG seeded - /// using this function with a fixed seed should produce the same sequence - /// of output in the future and on different architectures (with for example - /// different endianness). + /// All PRNG implementations should be reproducible unless otherwise noted: + /// given a fixed `seed`, the same sequence of output should be produced + /// on all runs, library versions and architectures (e.g. check endianness). + /// Any "value-breaking" changes to the generator should require bumping at + /// least the minor version and documentation of the change. /// - /// It is however not required that this function yield the same state as a + /// It is not required that this function yield the same state as a /// reference implementation of the PRNG given equivalent seed; if necessary /// another constructor replicating behaviour from a reference /// implementation can be added. @@ -297,17 +287,17 @@ pub trait SeedableRng: Sized { /// for example `0xBAD5EEDu32` or `0x0DDB1A5E5BAD5EEDu64` ("odd biases? bad /// seed"). This is assuming only a small number of values must be rejected. fn from_seed(seed: Self::Seed) -> Self; - + /// Create a new PRNG using a `u64` seed. - /// + /// /// This is a convenience-wrapper around `from_seed` to allow construction /// of any `SeedableRng` from a simple `u64` value. It is designed such that /// low Hamming Weight numbers like 0 and 1 can be used and should still /// result in good, independent seeds to the PRNG which is returned. - /// + /// /// This **is not suitable for cryptography**, as should be clear given that /// the input size is only 64 bits. - /// + /// /// Implementations for PRNGs *may* provide their own implementations of /// this function, but the default implementation should be good enough for /// all purposes. *Changing* the implementation of this function should be @@ -316,64 +306,80 @@ pub trait SeedableRng: Sized { // We use PCG32 to generate a u32 sequence, and copy to the seed const MUL: u64 = 6364136223846793005; const INC: u64 = 11634580027462260723; - + let mut seed = Self::Seed::default(); for chunk in seed.as_mut().chunks_mut(4) { // We advance the state first (to get away from the input value, // in case it has low Hamming Weight). state = state.wrapping_mul(MUL).wrapping_add(INC); - + // Use PCG output function with to_le to generate x: let xorshifted = (((state >> 18) ^ state) >> 27) as u32; let rot = (state >> 59) as u32; let x = xorshifted.rotate_right(rot).to_le(); - + unsafe { let p = &x as *const u32 as *const u8; copy_nonoverlapping(p, chunk.as_mut_ptr(), chunk.len()); } } - + Self::from_seed(seed) } - + /// Create a new PRNG seeded from another `Rng`. /// - /// This is the recommended way to initialize PRNGs with fresh entropy. The - /// [`FromEntropy`] trait provides a convenient `from_entropy` method - /// based on `from_rng`. - /// - /// Usage of this method is not recommended when reproducibility is required - /// since implementing PRNGs are not required to fix Endianness and are - /// allowed to modify implementations in new releases. - /// - /// It is important to use a good source of randomness to initialize the - /// PRNG. Cryptographic PRNG may be rendered insecure when seeded from a - /// non-cryptographic PRNG or with insufficient entropy. - /// Many non-cryptographic PRNGs will show statistical bias in their first - /// results if their seed numbers are small or if there is a simple pattern - /// between them. - /// - /// Prefer to seed from a strong external entropy source like [`OsRng`] or - /// from a cryptographic PRNG; if creating a new generator for cryptographic - /// uses you *must* seed from a strong source. - /// - /// Seeding a small PRNG from another small PRNG is possible, but - /// something to be careful with. An extreme example of how this can go - /// wrong is seeding an Xorshift RNG from another Xorshift RNG, which - /// will effectively clone the generator. In general seeding from a - /// generator which is hard to predict is probably okay. + /// This may be useful when needing to rapidly seed many PRNGs from a master + /// PRNG, and to allow forking of PRNGs. It may be considered deterministic. + /// + /// The master PRNG should be at least as high quality as the child PRNGs. + /// When seeding non-cryptographic child PRNGs, we recommend using a + /// different algorithm for the master PRNG (ideally a CSPRNG) to avoid + /// correlations between the child PRNGs. If this is not possible (e.g. + /// forking using small non-crypto PRNGs) ensure that your PRNG has a good + /// mixing function on the output or consider use of a hash function with + /// `from_seed`. + /// + /// Note that seeding `XorShiftRng` from another `XorShiftRng` provides an + /// extreme example of what can go wrong: the new PRNG will be a clone + /// of the parent. /// /// PRNG implementations are allowed to assume that a good RNG is provided /// for seeding, and that it is cryptographically secure when appropriate. - /// - /// [`FromEntropy`]: ../rand/trait.FromEntropy.html - /// [`OsRng`]: ../rand/rngs/struct.OsRng.html + /// As of `rand` 0.7 / `rand_core` 0.5, implementations overriding this + /// method should ensure the implementation satisfies reproducibility + /// (in prior versions this was not required). + /// + /// [`rand`]: https://docs.rs/rand + /// [`rand_os`]: https://docs.rs/rand_os fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, Error> { let mut seed = Self::Seed::default(); rng.try_fill_bytes(seed.as_mut())?; Ok(Self::from_seed(seed)) } + + /// Creates a new instance of the RNG seeded via [`getrandom`]. + /// + /// This method is the recommended way to construct non-deterministic PRNGs + /// since it is convenient and secure. + /// + /// In case the overhead of using [`getrandom`] to seed *many* PRNGs is an + /// issue, one may prefer to seed from a local PRNG, e.g. + /// `from_rng(thread_rng()).unwrap()`. + /// + /// # Panics + /// + /// If [`getrandom`] is unable to provide secure entropy this method will panic. + /// + /// [`getrandom`]: https://docs.rs/getrandom + #[cfg(feature="getrandom")] + fn from_entropy() -> Self { + let mut seed = Self::Seed::default(); + if let Err(err) = getrandom::getrandom(seed.as_mut()) { + panic!("from_entropy failed: {}", err); + } + Self::from_seed(seed) + } } // Implement `RngCore` for references to an `RngCore`. @@ -428,7 +434,7 @@ impl<R: RngCore + ?Sized> RngCore for Box<R> { } #[cfg(feature="std")] -impl std::io::Read for RngCore { +impl std::io::Read for dyn RngCore { fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> { self.try_fill_bytes(buf)?; Ok(buf.len()) @@ -445,7 +451,7 @@ impl<R: CryptoRng + ?Sized> CryptoRng for Box<R> {} #[cfg(test)] mod test { use super::*; - + #[test] fn test_seed_from_u64() { struct SeedableNum(u64); @@ -457,7 +463,7 @@ mod test { SeedableNum(x[0]) } } - + const N: usize = 8; const SEEDS: [u64; N] = [0u64, 1, 2, 3, 4, 8, 16, -1i64 as u64]; let mut results = [0u64; N]; @@ -465,21 +471,21 @@ mod test { let SeedableNum(x) = SeedableNum::seed_from_u64(*seed); results[i] = x; } - + for (i1, r1) in results.iter().enumerate() { let weight = r1.count_ones(); // This is the binomial distribution B(64, 0.5), so chance of // weight < 20 is binocdf(19, 64, 0.5) = 7.8e-4, and same for // weight > 44. assert!(weight >= 20 && weight <= 44); - + for (i2, r2) in results.iter().enumerate() { if i1 == i2 { continue; } let diff_weight = (r1 ^ r2).count_ones(); assert!(diff_weight >= 20); } } - + // value-breakage test: assert_eq!(results[0], 5029875928683246316); } diff --git a/rand/rand_core/src/os.rs b/rand/rand_core/src/os.rs new file mode 100644 index 0000000..fc23a57 --- /dev/null +++ b/rand/rand_core/src/os.rs @@ -0,0 +1,85 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Interface to the random number generator of the operating system. +// Note: keep this code in sync with the rand_os crate! + +use getrandom::getrandom; +use crate::{CryptoRng, RngCore, Error, impls}; + +/// A random number generator that retrieves randomness from from the +/// operating system. +/// +/// This is a zero-sized struct. It can be freely constructed with `OsRng`. +/// +/// The implementation is provided by the [getrandom] crate. Refer to +/// [getrandom] documentation for details. +/// +/// This struct is only available when specifying the crate feature `getrandom` +/// or `std`. When using the `rand` lib, it is also available as `rand::rngs::OsRng`. +/// +/// # Blocking and error handling +/// +/// It is possible that when used during early boot the first call to `OsRng` +/// will block until the system's RNG is initialised. It is also possible +/// (though highly unlikely) for `OsRng` to fail on some platforms, most +/// likely due to system mis-configuration. +/// +/// After the first successful call, it is highly unlikely that failures or +/// significant delays will occur (although performance should be expected to +/// be much slower than a user-space PRNG). +/// +/// # Usage example +/// ``` +/// use rand_core::{RngCore, OsRng}; +/// +/// let mut key = [0u8; 16]; +/// OsRng.fill_bytes(&mut key); +/// let random_u64 = OsRng.next_u64(); +/// ``` +/// +/// [getrandom]: https://crates.io/crates/getrandom +#[derive(Clone, Copy, Debug, Default)] +pub struct OsRng; + +impl CryptoRng for OsRng {} + +impl RngCore for OsRng { + fn next_u32(&mut self) -> u32 { + impls::next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_fill(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + if let Err(e) = self.try_fill_bytes(dest) { + panic!("Error: {}", e); + } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + getrandom(dest)?; + Ok(()) + } +} + +#[test] +fn test_os_rng() { + let x = OsRng.next_u64(); + let y = OsRng.next_u64(); + assert!(x != 0); + assert!(x != y); +} + +#[test] +fn test_construction() { + let mut rng = OsRng::default(); + assert!(rng.next_u64() != 0); +} diff --git a/rand/rand_distr/CHANGELOG.md b/rand/rand_distr/CHANGELOG.md new file mode 100644 index 0000000..376bb95 --- /dev/null +++ b/rand/rand_distr/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.1] - 2019-06-29 +- Update dependency to support Rand 0.7 +- Doc link fixes + +## [0.2.0] - 2019-06-06 +- Remove `new` constructors for zero-sized types +- Add Pert distribution +- Fix undefined behavior in `Poisson` +- Make all distributions return `Result`s instead of panicking +- Implement `f32` support for most distributions +- Rename `UnitSphereSurface` to `UnitSphere` +- Implement `UnitBall` and `UnitDisc` + +## [0.1.0] - 2019-06-06 +Initial release. This is equivalent to the code in `rand` 0.6.5. diff --git a/rand/rand_distr/COPYRIGHT b/rand/rand_distr/COPYRIGHT new file mode 100644 index 0000000..468d907 --- /dev/null +++ b/rand/rand_distr/COPYRIGHT @@ -0,0 +1,12 @@ +Copyrights in the Rand project are retained by their contributors. No +copyright assignment is required to contribute to the Rand project. + +For full authorship information, see the version control history. + +Except as otherwise noted (below and/or in individual files), Rand is +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. + +The Rand project includes code from the Rust project +published under these same licenses. diff --git a/rand/rand_distr/Cargo.toml b/rand/rand_distr/Cargo.toml new file mode 100644 index 0000000..315a5b0 --- /dev/null +++ b/rand/rand_distr/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "rand_distr" +version = "0.2.1" +authors = ["The Rand Project Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-random/rand" +documentation = "https://rust-random.github.io/rand/rand_distr/" +homepage = "https://crates.io/crates/rand_distr" +description = """ +Sampling from random number distributions +""" +keywords = ["random", "rng", "distribution", "probability"] +categories = ["algorithms"] +edition = "2018" + +[badges] +travis-ci = { repository = "rust-random/rand" } +appveyor = { repository = "rust-random/rand" } + +[dependencies] +rand = { path = "..", version = "0.7" } + +[dev-dependencies] +rand_pcg = { version = "0.2", path = "../rand_pcg" } +# Histogram implementation for testing uniformity +average = "0.9.2" diff --git a/rand/rand_distr/LICENSE-APACHE b/rand/rand_distr/LICENSE-APACHE new file mode 100644 index 0000000..17d7468 --- /dev/null +++ b/rand/rand_distr/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rand/rand_distr/LICENSE-MIT b/rand/rand_distr/LICENSE-MIT new file mode 100644 index 0000000..cf65607 --- /dev/null +++ b/rand/rand_distr/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright 2018 Developers of the Rand project + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/rand/rand_distr/README.md b/rand/rand_distr/README.md new file mode 100644 index 0000000..68acd2f --- /dev/null +++ b/rand/rand_distr/README.md @@ -0,0 +1,42 @@ +# rand_distr + +[![Build Status](https://travis-ci.org/rust-random/rand.svg?branch=master)](https://travis-ci.org/rust-random/rand) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-random/rand?svg=true)](https://ci.appveyor.com/project/rust-random/rand) +[![Latest version](https://img.shields.io/crates/v/rand_distr.svg)](https://crates.io/crates/rand_distr) +[[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) +[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_distr) +[![API](https://docs.rs/rand_distr/badge.svg)](https://docs.rs/rand_distr) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) + +Implements a full suite of random number distributions sampling routines. + +This crate is a super-set of the [rand::distributions] module, including support +for sampling from Beta, Binomial, Cauchy, ChiSquared, Dirichlet, exponential, +Fisher F, Gamma, Log-normal, Normal, Pareto, Poisson, StudentT, Triangular and +Weibull distributions, as well as sampling points from the unit circle and unit +sphere surface. + +It is worth mentioning the [statrs] crate which provides similar functionality +along with various support functions, including PDF and CDF computation. In +contrast, this `rand_distr` crate focusses on sampling from distributions. + +Unlike most Rand crates, `rand_distr` does not currently support `no_std`. + +Links: + +- [API documentation (master)](https://rust-random.github.io/rand/rand_distr) +- [API documentation (docs.rs)](https://docs.rs/rand_distr) +- [Changelog](CHANGELOG.md) +- [The Rand project](https://github.com/rust-random/rand) + + +[statrs]: https://github.com/boxtown/statrs +[rand::distributions]: https://rust-random.github.io/rand/rand/distributions/index.html + +## License + +`rand_distr` is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and +[COPYRIGHT](COPYRIGHT) for details. diff --git a/rand/benches/distributions.rs b/rand/rand_distr/benches/distributions.rs index 069a828..63bde36 100644 --- a/rand/benches/distributions.rs +++ b/rand/rand_distr/benches/distributions.rs @@ -8,35 +8,55 @@ #![feature(test)] -extern crate test; -extern crate rand; - const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; +use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128}; use test::Bencher; use std::time::Duration; -use rand::{Rng, FromEntropy}; -use rand::rngs::SmallRng; -use rand::distributions::*; +use rand::prelude::*; +use rand_distr::{*, weighted::WeightedIndex}; + +// At this time, distributions are optimised for 64-bit platforms. +use rand_pcg::Pcg64Mcg; macro_rules! distr_int { ($fnn:ident, $ty:ty, $distr:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); + let mut rng = Pcg64Mcg::from_entropy(); let distr = $distr; b.iter(|| { let mut accum = 0 as $ty; - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { let x: $ty = distr.sample(&mut rng); accum = accum.wrapping_add(x); } accum }); - b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; + } + } +} + +macro_rules! distr_nz_int { + ($fnn:ident, $tynz:ty, $ty:ty, $distr:expr) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = Pcg64Mcg::from_entropy(); + let distr = $distr; + + b.iter(|| { + let mut accum = 0 as $ty; + for _ in 0..RAND_BENCH_N { + let x: $tynz = distr.sample(&mut rng); + accum = accum.wrapping_add(x.get()); + } + accum + }); + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; } } } @@ -45,18 +65,18 @@ macro_rules! distr_float { ($fnn:ident, $ty:ty, $distr:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); + let mut rng = Pcg64Mcg::from_entropy(); let distr = $distr; b.iter(|| { let mut accum = 0.0; - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { let x: $ty = distr.sample(&mut rng); accum += x; } accum }); - b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; } } } @@ -65,18 +85,18 @@ macro_rules! distr_duration { ($fnn:ident, $distr:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); + let mut rng = Pcg64Mcg::from_entropy(); let distr = $distr; b.iter(|| { let mut accum = Duration::new(0, 0); - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { let x: Duration = distr.sample(&mut rng); accum = accum.checked_add(x).unwrap_or(Duration::new(u64::max_value(), 999_999_999)); } accum }); - b.bytes = size_of::<Duration>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<Duration>() as u64 * RAND_BENCH_N; } } } @@ -85,18 +105,18 @@ macro_rules! distr { ($fnn:ident, $ty:ty, $distr:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); + let mut rng = Pcg64Mcg::from_entropy(); let distr = $distr; b.iter(|| { let mut accum = 0u32; - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { let x: $ty = distr.sample(&mut rng); accum = accum.wrapping_add(x as u32); } accum }); - b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; } } } @@ -105,18 +125,18 @@ macro_rules! distr_arr { ($fnn:ident, $ty:ty, $distr:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); + let mut rng = Pcg64Mcg::from_entropy(); let distr = $distr; b.iter(|| { let mut accum = 0u32; - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { let x: $ty = distr.sample(&mut rng); accum = accum.wrapping_add(x[0] as u32); } accum }); - b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; } } } @@ -127,6 +147,11 @@ distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000)); distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000)); distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123)); distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789)); +distr_int!(distr_uniform_usize16, usize, Uniform::new(0usize, 0xb9d7)); +distr_int!(distr_uniform_usize32, usize, Uniform::new(0usize, 0x548c0f43)); +#[cfg(target_pointer_width = "64")] +distr_int!(distr_uniform_usize64, usize, Uniform::new(0usize, 0x3a42714f2bf927a8)); +distr_int!(distr_uniform_isize, isize, Uniform::new(-1060478432isize, 1858574057)); distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319)); distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319)); @@ -156,6 +181,11 @@ distr_int!(distr_standard_i16, i16, Standard); distr_int!(distr_standard_i32, i32, Standard); distr_int!(distr_standard_i64, i64, Standard); distr_int!(distr_standard_i128, i128, Standard); +distr_nz_int!(distr_standard_nz8, NonZeroU8, u8, Standard); +distr_nz_int!(distr_standard_nz16, NonZeroU16, u16, Standard); +distr_nz_int!(distr_standard_nz32, NonZeroU32, u32, Standard); +distr_nz_int!(distr_standard_nz64, NonZeroU64, u64, Standard); +distr_nz_int!(distr_standard_nz128, NonZeroU128, u128, Standard); distr!(distr_standard_bool, bool, Standard); distr!(distr_standard_alphanumeric, char, Alphanumeric); @@ -169,17 +199,19 @@ distr_float!(distr_openclosed01_f32, f32, OpenClosed01); distr_float!(distr_openclosed01_f64, f64, OpenClosed01); // distributions -distr_float!(distr_exp, f64, Exp::new(1.23 * 4.56)); -distr_float!(distr_normal, f64, Normal::new(-1.23, 4.56)); -distr_float!(distr_log_normal, f64, LogNormal::new(-1.23, 4.56)); -distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0)); -distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0)); -distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9)); -distr_int!(distr_binomial, u64, Binomial::new(20, 0.7)); -distr_int!(distr_poisson, u64, Poisson::new(4.0)); -distr!(distr_bernoulli, bool, Bernoulli::new(0.18)); -distr_arr!(distr_circle, [f64; 2], UnitCircle::new()); -distr_arr!(distr_sphere_surface, [f64; 3], UnitSphereSurface::new()); +distr_float!(distr_exp, f64, Exp::new(1.23 * 4.56).unwrap()); +distr_float!(distr_normal, f64, Normal::new(-1.23, 4.56).unwrap()); +distr_float!(distr_log_normal, f64, LogNormal::new(-1.23, 4.56).unwrap()); +distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0).unwrap()); +distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0).unwrap()); +distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9).unwrap()); +distr_float!(distr_triangular, f64, Triangular::new(0., 1., 0.9).unwrap()); +distr_int!(distr_binomial, u64, Binomial::new(20, 0.7).unwrap()); +distr_int!(distr_binomial_small, u64, Binomial::new(1000000, 1e-30).unwrap()); +distr_int!(distr_poisson, u64, Poisson::new(4.0).unwrap()); +distr!(distr_bernoulli, bool, Bernoulli::new(0.18).unwrap()); +distr_arr!(distr_circle, [f64; 2], UnitCircle); +distr_arr!(distr_sphere, [f64; 3], UnitSphere); // Weighted distr_int!(distr_weighted_i8, usize, WeightedIndex::new(&[1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap()); @@ -187,24 +219,29 @@ distr_int!(distr_weighted_u32, usize, WeightedIndex::new(&[1u32, 2, 3, 4, 12, 0, distr_int!(distr_weighted_f64, usize, WeightedIndex::new(&[1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap()); distr_int!(distr_weighted_large_set, usize, WeightedIndex::new((0..10000).rev().chain(1..10001)).unwrap()); +distr_int!(distr_weighted_alias_method_i8, usize, weighted::alias_method::WeightedIndex::new(vec![1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap()); +distr_int!(distr_weighted_alias_method_u32, usize, weighted::alias_method::WeightedIndex::new(vec![1u32, 2, 3, 4, 12, 0, 2, 1]).unwrap()); +distr_int!(distr_weighted_alias_method_f64, usize, weighted::alias_method::WeightedIndex::new(vec![1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap()); +distr_int!(distr_weighted_alias_method_large_set, usize, weighted::alias_method::WeightedIndex::new((0..10000).rev().chain(1..10001).collect()).unwrap()); + // construct and sample from a range macro_rules! gen_range_int { ($fnn:ident, $ty:ident, $low:expr, $high:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); + let mut rng = Pcg64Mcg::from_entropy(); b.iter(|| { let mut high = $high; let mut accum: $ty = 0; - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { accum = accum.wrapping_add(rng.gen_range($low, high)); // force recalculation of range each time high = high.wrapping_add(1) & std::$ty::MAX; } accum }); - b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; } } } @@ -220,13 +257,13 @@ macro_rules! gen_range_float { ($fnn:ident, $ty:ident, $low:expr, $high:expr) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); + let mut rng = Pcg64Mcg::from_entropy(); b.iter(|| { let mut high = $high; let mut low = $low; let mut accum: $ty = 0.0; - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { accum += rng.gen_range(low, high); // force recalculation of range each time low += 0.9; @@ -234,7 +271,7 @@ macro_rules! gen_range_float { } accum }); - b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; } } } @@ -244,16 +281,36 @@ gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12); #[bench] fn dist_iter(b: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); - let distr = Normal::new(-2.71828, 3.14159); + let mut rng = Pcg64Mcg::from_entropy(); + let distr = Normal::new(-2.71828, 3.14159).unwrap(); let mut iter = distr.sample_iter(&mut rng); b.iter(|| { let mut accum = 0.0; - for _ in 0..::RAND_BENCH_N { + for _ in 0..RAND_BENCH_N { accum += iter.next().unwrap(); } accum }); - b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N; + b.bytes = size_of::<f64>() as u64 * RAND_BENCH_N; } + +macro_rules! sample_binomial { + ($name:ident, $n:expr, $p:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()).unwrap(); + let (n, p) = ($n, $p); + b.iter(|| { + let d = Binomial::new(n, p).unwrap(); + rng.sample(d) + }) + } + } +} + +sample_binomial!(misc_binomial_1, 1, 0.9); +sample_binomial!(misc_binomial_10, 10, 0.9); +sample_binomial!(misc_binomial_100, 100, 0.99); +sample_binomial!(misc_binomial_1000, 1000, 0.01); +sample_binomial!(misc_binomial_1e12, 1000_000_000_000, 0.2); diff --git a/rand/rand_distr/src/binomial.rs b/rand/rand_distr/src/binomial.rs new file mode 100644 index 0000000..0e6bf9a --- /dev/null +++ b/rand/rand_distr/src/binomial.rs @@ -0,0 +1,329 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2016-2017 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The binomial distribution. + +use rand::Rng; +use crate::{Distribution, Uniform}; + +/// The binomial distribution `Binomial(n, p)`. +/// +/// This distribution has density function: +/// `f(k) = n!/(k! (n-k)!) p^k (1-p)^(n-k)` for `k >= 0`. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{Binomial, Distribution}; +/// +/// let bin = Binomial::new(20, 0.3).unwrap(); +/// let v = bin.sample(&mut rand::thread_rng()); +/// println!("{} is from a binomial distribution", v); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Binomial { + /// Number of trials. + n: u64, + /// Probability of success. + p: f64, +} + +/// Error type returned from `Binomial::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `p < 0` or `nan`. + ProbabilityTooSmall, + /// `p > 1`. + ProbabilityTooLarge, +} + +impl Binomial { + /// Construct a new `Binomial` with the given shape parameters `n` (number + /// of trials) and `p` (probability of success). + pub fn new(n: u64, p: f64) -> Result<Binomial, Error> { + if !(p >= 0.0) { + return Err(Error::ProbabilityTooSmall); + } + if !(p <= 1.0) { + return Err(Error::ProbabilityTooLarge); + } + Ok(Binomial { n, p }) + } +} + +/// Convert a `f64` to an `i64`, panicing on overflow. +// In the future (Rust 1.34), this might be replaced with `TryFrom`. +fn f64_to_i64(x: f64) -> i64 { + assert!(x < (::std::i64::MAX as f64)); + x as i64 +} + +impl Distribution<u64> for Binomial { + #[allow(clippy::many_single_char_names)] // Same names as in the reference. + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 { + // Handle these values directly. + if self.p == 0.0 { + return 0; + } else if self.p == 1.0 { + return self.n; + } + + // The binomial distribution is symmetrical with respect to p -> 1-p, + // k -> n-k switch p so that it is less than 0.5 - this allows for lower + // expected values we will just invert the result at the end + let p = if self.p <= 0.5 { + self.p + } else { + 1.0 - self.p + }; + + let result; + let q = 1. - p; + + // For small n * min(p, 1 - p), the BINV algorithm based on the inverse + // transformation of the binomial distribution is efficient. Otherwise, + // the BTPE algorithm is used. + // + // Voratas Kachitvichyanukul and Bruce W. Schmeiser. 1988. Binomial + // random variate generation. Commun. ACM 31, 2 (February 1988), + // 216-222. http://dx.doi.org/10.1145/42372.42381 + + // Threshold for prefering the BINV algorithm. The paper suggests 10, + // Ranlib uses 30, and GSL uses 14. + const BINV_THRESHOLD: f64 = 10.; + + if (self.n as f64) * p < BINV_THRESHOLD && + self.n <= (::std::i32::MAX as u64) { + // Use the BINV algorithm. + let s = p / q; + let a = ((self.n + 1) as f64) * s; + let mut r = q.powi(self.n as i32); + let mut u: f64 = rng.gen(); + let mut x = 0; + while u > r as f64 { + u -= r; + x += 1; + r *= a / (x as f64) - s; + } + result = x; + } else { + // Use the BTPE algorithm. + + // Threshold for using the squeeze algorithm. This can be freely + // chosen based on performance. Ranlib and GSL use 20. + const SQUEEZE_THRESHOLD: i64 = 20; + + // Step 0: Calculate constants as functions of `n` and `p`. + let n = self.n as f64; + let np = n * p; + let npq = np * q; + let f_m = np + p; + let m = f64_to_i64(f_m); + // radius of triangle region, since height=1 also area of region + let p1 = (2.195 * npq.sqrt() - 4.6 * q).floor() + 0.5; + // tip of triangle + let x_m = (m as f64) + 0.5; + // left edge of triangle + let x_l = x_m - p1; + // right edge of triangle + let x_r = x_m + p1; + let c = 0.134 + 20.5 / (15.3 + (m as f64)); + // p1 + area of parallelogram region + let p2 = p1 * (1. + 2. * c); + + fn lambda(a: f64) -> f64 { + a * (1. + 0.5 * a) + } + + let lambda_l = lambda((f_m - x_l) / (f_m - x_l * p)); + let lambda_r = lambda((x_r - f_m) / (x_r * q)); + // p1 + area of left tail + let p3 = p2 + c / lambda_l; + // p1 + area of right tail + let p4 = p3 + c / lambda_r; + + // return value + let mut y: i64; + + let gen_u = Uniform::new(0., p4); + let gen_v = Uniform::new(0., 1.); + + loop { + // Step 1: Generate `u` for selecting the region. If region 1 is + // selected, generate a triangularly distributed variate. + let u = gen_u.sample(rng); + let mut v = gen_v.sample(rng); + if !(u > p1) { + y = f64_to_i64(x_m - p1 * v + u); + break; + } + + if !(u > p2) { + // Step 2: Region 2, parallelograms. Check if region 2 is + // used. If so, generate `y`. + let x = x_l + (u - p1) / c; + v = v * c + 1.0 - (x - x_m).abs() / p1; + if v > 1. { + continue; + } else { + y = f64_to_i64(x); + } + } else if !(u > p3) { + // Step 3: Region 3, left exponential tail. + y = f64_to_i64(x_l + v.ln() / lambda_l); + if y < 0 { + continue; + } else { + v *= (u - p2) * lambda_l; + } + } else { + // Step 4: Region 4, right exponential tail. + y = f64_to_i64(x_r - v.ln() / lambda_r); + if y > 0 && (y as u64) > self.n { + continue; + } else { + v *= (u - p3) * lambda_r; + } + } + + // Step 5: Acceptance/rejection comparison. + + // Step 5.0: Test for appropriate method of evaluating f(y). + let k = (y - m).abs(); + if !(k > SQUEEZE_THRESHOLD && (k as f64) < 0.5 * npq - 1.) { + // Step 5.1: Evaluate f(y) via the recursive relationship. Start the + // search from the mode. + let s = p / q; + let a = s * (n + 1.); + let mut f = 1.0; + if m < y { + let mut i = m; + loop { + i += 1; + f *= a / (i as f64) - s; + if i == y { + break; + } + } + } else if m > y { + let mut i = y; + loop { + i += 1; + f /= a / (i as f64) - s; + if i == m { + break; + } + } + } + if v > f { + continue; + } else { + break; + } + } + + // Step 5.2: Squeezing. Check the value of ln(v) againts upper and + // lower bound of ln(f(y)). + let k = k as f64; + let rho = (k / npq) * ((k * (k / 3. + 0.625) + 1./6.) / npq + 0.5); + let t = -0.5 * k*k / npq; + let alpha = v.ln(); + if alpha < t - rho { + break; + } + if alpha > t + rho { + continue; + } + + // Step 5.3: Final acceptance/rejection test. + let x1 = (y + 1) as f64; + let f1 = (m + 1) as f64; + let z = (f64_to_i64(n) + 1 - m) as f64; + let w = (f64_to_i64(n) - y + 1) as f64; + + fn stirling(a: f64) -> f64 { + let a2 = a * a; + (13860. - (462. - (132. - (99. - 140. / a2) / a2) / a2) / a2) / a / 166320. + } + + if alpha > x_m * (f1 / x1).ln() + + (n - (m as f64) + 0.5) * (z / w).ln() + + ((y - m) as f64) * (w * p / (x1 * q)).ln() + // We use the signs from the GSL implementation, which are + // different than the ones in the reference. According to + // the GSL authors, the new signs were verified to be + // correct by one of the original designers of the + // algorithm. + + stirling(f1) + stirling(z) - stirling(x1) - stirling(w) + { + continue; + } + + break; + } + assert!(y >= 0); + result = y as u64; + } + + // Invert the result for p < 0.5. + if p != self.p { + self.n - result + } else { + result + } + } +} + +#[cfg(test)] +mod test { + use rand::Rng; + use crate::Distribution; + use super::Binomial; + + fn test_binomial_mean_and_variance<R: Rng>(n: u64, p: f64, rng: &mut R) { + let binomial = Binomial::new(n, p).unwrap(); + + let expected_mean = n as f64 * p; + let expected_variance = n as f64 * p * (1.0 - p); + + let mut results = [0.0; 1000]; + for i in results.iter_mut() { *i = binomial.sample(rng) as f64; } + + let mean = results.iter().sum::<f64>() / results.len() as f64; + assert!((mean as f64 - expected_mean).abs() < expected_mean / 50.0); + + let variance = + results.iter().map(|x| (x - mean) * (x - mean)).sum::<f64>() + / results.len() as f64; + assert!((variance - expected_variance).abs() < expected_variance / 10.0); + } + + #[test] + fn test_binomial() { + let mut rng = crate::test::rng(351); + test_binomial_mean_and_variance(150, 0.1, &mut rng); + test_binomial_mean_and_variance(70, 0.6, &mut rng); + test_binomial_mean_and_variance(40, 0.5, &mut rng); + test_binomial_mean_and_variance(20, 0.7, &mut rng); + test_binomial_mean_and_variance(20, 0.5, &mut rng); + } + + #[test] + fn test_binomial_end_points() { + let mut rng = crate::test::rng(352); + assert_eq!(rng.sample(Binomial::new(20, 0.0).unwrap()), 0); + assert_eq!(rng.sample(Binomial::new(20, 1.0).unwrap()), 20); + } + + #[test] + #[should_panic] + fn test_binomial_invalid_lambda_neg() { + Binomial::new(20, -10.0).unwrap(); + } +} diff --git a/rand/rand_distr/src/cauchy.rs b/rand/rand_distr/src/cauchy.rs new file mode 100644 index 0000000..6b0e7c6 --- /dev/null +++ b/rand/rand_distr/src/cauchy.rs @@ -0,0 +1,120 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2016-2017 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Cauchy distribution. + +use rand::Rng; +use crate::{Distribution, Standard}; +use crate::utils::Float; + +/// The Cauchy distribution `Cauchy(median, scale)`. +/// +/// This distribution has a density function: +/// `f(x) = 1 / (pi * scale * (1 + ((x - median) / scale)^2))` +/// +/// # Example +/// +/// ``` +/// use rand_distr::{Cauchy, Distribution}; +/// +/// let cau = Cauchy::new(2.0, 5.0).unwrap(); +/// let v = cau.sample(&mut rand::thread_rng()); +/// println!("{} is from a Cauchy(2, 5) distribution", v); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Cauchy<N> { + median: N, + scale: N, +} + +/// Error type returned from `Cauchy::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `scale <= 0` or `nan`. + ScaleTooSmall, +} + +impl<N: Float> Cauchy<N> +where Standard: Distribution<N> +{ + /// Construct a new `Cauchy` with the given shape parameters + /// `median` the peak location and `scale` the scale factor. + pub fn new(median: N, scale: N) -> Result<Cauchy<N>, Error> { + if !(scale > N::from(0.0)) { + return Err(Error::ScaleTooSmall); + } + Ok(Cauchy { + median, + scale + }) + } +} + +impl<N: Float> Distribution<N> for Cauchy<N> +where Standard: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + // sample from [0, 1) + let x = Standard.sample(rng); + // get standard cauchy random number + // note that π/2 is not exactly representable, even if x=0.5 the result is finite + let comp_dev = (N::pi() * x).tan(); + // shift and scale according to parameters + self.median + self.scale * comp_dev + } +} + +#[cfg(test)] +mod test { + use crate::Distribution; + use super::Cauchy; + + fn median(mut numbers: &mut [f64]) -> f64 { + sort(&mut numbers); + let mid = numbers.len() / 2; + numbers[mid] + } + + fn sort(numbers: &mut [f64]) { + numbers.sort_by(|a, b| a.partial_cmp(b).unwrap()); + } + + #[test] + fn test_cauchy_averages() { + // NOTE: given that the variance and mean are undefined, + // this test does not have any rigorous statistical meaning. + let cauchy = Cauchy::new(10.0, 5.0).unwrap(); + let mut rng = crate::test::rng(123); + let mut numbers: [f64; 1000] = [0.0; 1000]; + let mut sum = 0.0; + for i in 0..1000 { + numbers[i] = cauchy.sample(&mut rng); + sum += numbers[i]; + } + let median = median(&mut numbers); + println!("Cauchy median: {}", median); + assert!((median - 10.0).abs() < 0.4); // not 100% certain, but probable enough + let mean = sum / 1000.0; + println!("Cauchy mean: {}", mean); + // for a Cauchy distribution the mean should not converge + assert!((mean - 10.0).abs() > 0.4); // not 100% certain, but probable enough + } + + #[test] + #[should_panic] + fn test_cauchy_invalid_scale_zero() { + Cauchy::new(0.0, 0.0).unwrap(); + } + + #[test] + #[should_panic] + fn test_cauchy_invalid_scale_neg() { + Cauchy::new(0.0, -10.0).unwrap(); + } +} diff --git a/rand/rand_distr/src/dirichlet.rs b/rand/rand_distr/src/dirichlet.rs new file mode 100644 index 0000000..71cf73c --- /dev/null +++ b/rand/rand_distr/src/dirichlet.rs @@ -0,0 +1,154 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2013 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The dirichlet distribution. + +use rand::Rng; +use crate::{Distribution, Gamma, StandardNormal, Exp1, Open01}; +use crate::utils::Float; + +/// The dirichelet distribution `Dirichlet(alpha)`. +/// +/// The Dirichlet distribution is a family of continuous multivariate +/// probability distributions parameterized by a vector alpha of positive reals. +/// It is a multivariate generalization of the beta distribution. +/// +/// # Example +/// +/// ``` +/// use rand::prelude::*; +/// use rand_distr::Dirichlet; +/// +/// let dirichlet = Dirichlet::new(vec![1.0, 2.0, 3.0]).unwrap(); +/// let samples = dirichlet.sample(&mut rand::thread_rng()); +/// println!("{:?} is from a Dirichlet([1.0, 2.0, 3.0]) distribution", samples); +/// ``` +#[derive(Clone, Debug)] +pub struct Dirichlet<N> { + /// Concentration parameters (alpha) + alpha: Vec<N>, +} + +/// Error type returned from `Dirchlet::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `alpha.len() < 2`. + AlphaTooShort, + /// `alpha <= 0.0` or `nan`. + AlphaTooSmall, + /// `size < 2`. + SizeTooSmall, +} + +impl<N: Float> Dirichlet<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + /// Construct a new `Dirichlet` with the given alpha parameter `alpha`. + /// + /// Requires `alpha.len() >= 2`. + #[inline] + pub fn new<V: Into<Vec<N>>>(alpha: V) -> Result<Dirichlet<N>, Error> { + let a = alpha.into(); + if a.len() < 2 { + return Err(Error::AlphaTooShort); + } + for &ai in &a { + if !(ai > N::from(0.0)) { + return Err(Error::AlphaTooSmall); + } + } + + Ok(Dirichlet { alpha: a }) + } + + /// Construct a new `Dirichlet` with the given shape parameter `alpha` and `size`. + /// + /// Requires `size >= 2`. + #[inline] + pub fn new_with_size(alpha: N, size: usize) -> Result<Dirichlet<N>, Error> { + if !(alpha > N::from(0.0)) { + return Err(Error::AlphaTooSmall); + } + if size < 2 { + return Err(Error::SizeTooSmall); + } + Ok(Dirichlet { + alpha: vec![alpha; size], + }) + } +} + +impl<N: Float> Distribution<Vec<N>> for Dirichlet<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec<N> { + let n = self.alpha.len(); + let mut samples = vec![N::from(0.0); n]; + let mut sum = N::from(0.0); + + for (s, &a) in samples.iter_mut().zip(self.alpha.iter()) { + let g = Gamma::new(a, N::from(1.0)).unwrap(); + *s = g.sample(rng); + sum += *s; + } + let invacc = N::from(1.0) / sum; + for s in samples.iter_mut() { + *s *= invacc; + } + samples + } +} + +#[cfg(test)] +mod test { + use super::Dirichlet; + use crate::Distribution; + + #[test] + fn test_dirichlet() { + let d = Dirichlet::new(vec![1.0, 2.0, 3.0]).unwrap(); + let mut rng = crate::test::rng(221); + let samples = d.sample(&mut rng); + let _: Vec<f64> = samples + .into_iter() + .map(|x| { + assert!(x > 0.0); + x + }) + .collect(); + } + + #[test] + fn test_dirichlet_with_param() { + let alpha = 0.5f64; + let size = 2; + let d = Dirichlet::new_with_size(alpha, size).unwrap(); + let mut rng = crate::test::rng(221); + let samples = d.sample(&mut rng); + let _: Vec<f64> = samples + .into_iter() + .map(|x| { + assert!(x > 0.0); + x + }) + .collect(); + } + + #[test] + #[should_panic] + fn test_dirichlet_invalid_length() { + Dirichlet::new_with_size(0.5f64, 1).unwrap(); + } + + #[test] + #[should_panic] + fn test_dirichlet_invalid_alpha() { + Dirichlet::new_with_size(0.0f64, 2).unwrap(); + } +} diff --git a/rand/rand_distr/src/exponential.rs b/rand/rand_distr/src/exponential.rs new file mode 100644 index 0000000..8322489 --- /dev/null +++ b/rand/rand_distr/src/exponential.rs @@ -0,0 +1,145 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2013 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The exponential distribution. + +use rand::Rng; +use crate::{ziggurat_tables, Distribution}; +use crate::utils::{ziggurat, Float}; + +/// Samples floating-point numbers according to the exponential distribution, +/// with rate parameter `λ = 1`. This is equivalent to `Exp::new(1.0)` or +/// sampling with `-rng.gen::<f64>().ln()`, but faster. +/// +/// See `Exp` for the general exponential distribution. +/// +/// Implemented via the ZIGNOR variant[^1] of the Ziggurat method. The exact +/// description in the paper was adjusted to use tables for the exponential +/// distribution rather than normal. +/// +/// [^1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to +/// Generate Normal Random Samples*]( +/// https://www.doornik.com/research/ziggurat.pdf). +/// Nuffield College, Oxford +/// +/// # Example +/// ``` +/// use rand::prelude::*; +/// use rand_distr::Exp1; +/// +/// let val: f64 = thread_rng().sample(Exp1); +/// println!("{}", val); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Exp1; + +impl Distribution<f32> for Exp1 { + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f32 { + // TODO: use optimal 32-bit implementation + let x: f64 = self.sample(rng); + x as f32 + } +} + +// This could be done via `-rng.gen::<f64>().ln()` but that is slower. +impl Distribution<f64> for Exp1 { + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 { + #[inline] + fn pdf(x: f64) -> f64 { + (-x).exp() + } + #[inline] + fn zero_case<R: Rng + ?Sized>(rng: &mut R, _u: f64) -> f64 { + ziggurat_tables::ZIG_EXP_R - rng.gen::<f64>().ln() + } + + ziggurat(rng, false, + &ziggurat_tables::ZIG_EXP_X, + &ziggurat_tables::ZIG_EXP_F, + pdf, zero_case) + } +} + +/// The exponential distribution `Exp(lambda)`. +/// +/// This distribution has density function: `f(x) = lambda * exp(-lambda * x)` +/// for `x > 0`. +/// +/// Note that [`Exp1`](crate::Exp1) is an optimised implementation for `lambda = 1`. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{Exp, Distribution}; +/// +/// let exp = Exp::new(2.0).unwrap(); +/// let v = exp.sample(&mut rand::thread_rng()); +/// println!("{} is from a Exp(2) distribution", v); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Exp<N> { + /// `lambda` stored as `1/lambda`, since this is what we scale by. + lambda_inverse: N +} + +/// Error type returned from `Exp::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `lambda <= 0` or `nan`. + LambdaTooSmall, +} + +impl<N: Float> Exp<N> +where Exp1: Distribution<N> +{ + /// Construct a new `Exp` with the given shape parameter + /// `lambda`. + #[inline] + pub fn new(lambda: N) -> Result<Exp<N>, Error> { + if !(lambda > N::from(0.0)) { + return Err(Error::LambdaTooSmall); + } + Ok(Exp { lambda_inverse: N::from(1.0) / lambda }) + } +} + +impl<N: Float> Distribution<N> for Exp<N> +where Exp1: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + rng.sample(Exp1) * self.lambda_inverse + } +} + +#[cfg(test)] +mod test { + use crate::Distribution; + use super::Exp; + + #[test] + fn test_exp() { + let exp = Exp::new(10.0).unwrap(); + let mut rng = crate::test::rng(221); + for _ in 0..1000 { + assert!(exp.sample(&mut rng) >= 0.0); + } + } + #[test] + #[should_panic] + fn test_exp_invalid_lambda_zero() { + Exp::new(0.0).unwrap(); + } + #[test] + #[should_panic] + fn test_exp_invalid_lambda_neg() { + Exp::new(-10.0).unwrap(); + } +} diff --git a/rand/rand_distr/src/gamma.rs b/rand/rand_distr/src/gamma.rs new file mode 100644 index 0000000..4018361 --- /dev/null +++ b/rand/rand_distr/src/gamma.rs @@ -0,0 +1,485 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2013 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Gamma and derived distributions. + +use self::GammaRepr::*; +use self::ChiSquaredRepr::*; + +use rand::Rng; +use crate::normal::StandardNormal; +use crate::{Distribution, Exp1, Exp, Open01}; +use crate::utils::Float; + +/// The Gamma distribution `Gamma(shape, scale)` distribution. +/// +/// The density function of this distribution is +/// +/// ```text +/// f(x) = x^(k - 1) * exp(-x / θ) / (Γ(k) * θ^k) +/// ``` +/// +/// where `Γ` is the Gamma function, `k` is the shape and `θ` is the +/// scale and both `k` and `θ` are strictly positive. +/// +/// The algorithm used is that described by Marsaglia & Tsang 2000[^1], +/// falling back to directly sampling from an Exponential for `shape +/// == 1`, and using the boosting technique described in that paper for +/// `shape < 1`. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{Distribution, Gamma}; +/// +/// let gamma = Gamma::new(2.0, 5.0).unwrap(); +/// let v = gamma.sample(&mut rand::thread_rng()); +/// println!("{} is from a Gamma(2, 5) distribution", v); +/// ``` +/// +/// [^1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method for +/// Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3 +/// (September 2000), 363-372. +/// DOI:[10.1145/358407.358414](https://doi.acm.org/10.1145/358407.358414) +#[derive(Clone, Copy, Debug)] +pub struct Gamma<N> { + repr: GammaRepr<N>, +} + +/// Error type returned from `Gamma::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `shape <= 0` or `nan`. + ShapeTooSmall, + /// `scale <= 0` or `nan`. + ScaleTooSmall, + /// `1 / scale == 0`. + ScaleTooLarge, +} + +#[derive(Clone, Copy, Debug)] +enum GammaRepr<N> { + Large(GammaLargeShape<N>), + One(Exp<N>), + Small(GammaSmallShape<N>) +} + +// These two helpers could be made public, but saving the +// match-on-Gamma-enum branch from using them directly (e.g. if one +// knows that the shape is always > 1) doesn't appear to be much +// faster. + +/// Gamma distribution where the shape parameter is less than 1. +/// +/// Note, samples from this require a compulsory floating-point `pow` +/// call, which makes it significantly slower than sampling from a +/// gamma distribution where the shape parameter is greater than or +/// equal to 1. +/// +/// See `Gamma` for sampling from a Gamma distribution with general +/// shape parameters. +#[derive(Clone, Copy, Debug)] +struct GammaSmallShape<N> { + inv_shape: N, + large_shape: GammaLargeShape<N> +} + +/// Gamma distribution where the shape parameter is larger than 1. +/// +/// See `Gamma` for sampling from a Gamma distribution with general +/// shape parameters. +#[derive(Clone, Copy, Debug)] +struct GammaLargeShape<N> { + scale: N, + c: N, + d: N +} + +impl<N: Float> Gamma<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + /// Construct an object representing the `Gamma(shape, scale)` + /// distribution. + #[inline] + pub fn new(shape: N, scale: N) -> Result<Gamma<N>, Error> { + if !(shape > N::from(0.0)) { + return Err(Error::ShapeTooSmall); + } + if !(scale > N::from(0.0)) { + return Err(Error::ScaleTooSmall); + } + + let repr = if shape == N::from(1.0) { + One(Exp::new(N::from(1.0) / scale).map_err(|_| Error::ScaleTooLarge)?) + } else if shape < N::from(1.0) { + Small(GammaSmallShape::new_raw(shape, scale)) + } else { + Large(GammaLargeShape::new_raw(shape, scale)) + }; + Ok(Gamma { repr }) + } +} + +impl<N: Float> GammaSmallShape<N> +where StandardNormal: Distribution<N>, Open01: Distribution<N> +{ + fn new_raw(shape: N, scale: N) -> GammaSmallShape<N> { + GammaSmallShape { + inv_shape: N::from(1.0) / shape, + large_shape: GammaLargeShape::new_raw(shape + N::from(1.0), scale) + } + } +} + +impl<N: Float> GammaLargeShape<N> +where StandardNormal: Distribution<N>, Open01: Distribution<N> +{ + fn new_raw(shape: N, scale: N) -> GammaLargeShape<N> { + let d = shape - N::from(1. / 3.); + GammaLargeShape { + scale, + c: N::from(1.0) / (N::from(9.) * d).sqrt(), + d + } + } +} + +impl<N: Float> Distribution<N> for Gamma<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + match self.repr { + Small(ref g) => g.sample(rng), + One(ref g) => g.sample(rng), + Large(ref g) => g.sample(rng), + } + } +} +impl<N: Float> Distribution<N> for GammaSmallShape<N> +where StandardNormal: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + let u: N = rng.sample(Open01); + + self.large_shape.sample(rng) * u.powf(self.inv_shape) + } +} +impl<N: Float> Distribution<N> for GammaLargeShape<N> +where StandardNormal: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + // Marsaglia & Tsang method, 2000 + loop { + let x: N = rng.sample(StandardNormal); + let v_cbrt = N::from(1.0) + self.c * x; + if v_cbrt <= N::from(0.0) { // a^3 <= 0 iff a <= 0 + continue + } + + let v = v_cbrt * v_cbrt * v_cbrt; + let u: N = rng.sample(Open01); + + let x_sqr = x * x; + if u < N::from(1.0) - N::from(0.0331) * x_sqr * x_sqr || + u.ln() < N::from(0.5) * x_sqr + self.d * (N::from(1.0) - v + v.ln()) + { + return self.d * v * self.scale + } + } + } +} + +/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of +/// freedom. +/// +/// For `k > 0` integral, this distribution is the sum of the squares +/// of `k` independent standard normal random variables. For other +/// `k`, this uses the equivalent characterisation +/// `χ²(k) = Gamma(k/2, 2)`. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{ChiSquared, Distribution}; +/// +/// let chi = ChiSquared::new(11.0).unwrap(); +/// let v = chi.sample(&mut rand::thread_rng()); +/// println!("{} is from a χ²(11) distribution", v) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct ChiSquared<N> { + repr: ChiSquaredRepr<N>, +} + +/// Error type returned from `ChiSquared::new` and `StudentT::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ChiSquaredError { + /// `0.5 * k <= 0` or `nan`. + DoFTooSmall, +} + +#[derive(Clone, Copy, Debug)] +enum ChiSquaredRepr<N> { + // k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1, + // e.g. when alpha = 1/2 as it would be for this case, so special- + // casing and using the definition of N(0,1)^2 is faster. + DoFExactlyOne, + DoFAnythingElse(Gamma<N>), +} + +impl<N: Float> ChiSquared<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + /// Create a new chi-squared distribution with degrees-of-freedom + /// `k`. + pub fn new(k: N) -> Result<ChiSquared<N>, ChiSquaredError> { + let repr = if k == N::from(1.0) { + DoFExactlyOne + } else { + if !(N::from(0.5) * k > N::from(0.0)) { + return Err(ChiSquaredError::DoFTooSmall); + } + DoFAnythingElse(Gamma::new(N::from(0.5) * k, N::from(2.0)).unwrap()) + }; + Ok(ChiSquared { repr }) + } +} +impl<N: Float> Distribution<N> for ChiSquared<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + match self.repr { + DoFExactlyOne => { + // k == 1 => N(0,1)^2 + let norm: N = rng.sample(StandardNormal); + norm * norm + } + DoFAnythingElse(ref g) => g.sample(rng) + } + } +} + +/// The Fisher F distribution `F(m, n)`. +/// +/// This distribution is equivalent to the ratio of two normalised +/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) / +/// (χ²(n)/n)`. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{FisherF, Distribution}; +/// +/// let f = FisherF::new(2.0, 32.0).unwrap(); +/// let v = f.sample(&mut rand::thread_rng()); +/// println!("{} is from an F(2, 32) distribution", v) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct FisherF<N> { + numer: ChiSquared<N>, + denom: ChiSquared<N>, + // denom_dof / numer_dof so that this can just be a straight + // multiplication, rather than a division. + dof_ratio: N, +} + +/// Error type returned from `FisherF::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FisherFError { + /// `m <= 0` or `nan`. + MTooSmall, + /// `n <= 0` or `nan`. + NTooSmall, +} + +impl<N: Float> FisherF<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + /// Create a new `FisherF` distribution, with the given parameter. + pub fn new(m: N, n: N) -> Result<FisherF<N>, FisherFError> { + if !(m > N::from(0.0)) { + return Err(FisherFError::MTooSmall); + } + if !(n > N::from(0.0)) { + return Err(FisherFError::NTooSmall); + } + + Ok(FisherF { + numer: ChiSquared::new(m).unwrap(), + denom: ChiSquared::new(n).unwrap(), + dof_ratio: n / m + }) + } +} +impl<N: Float> Distribution<N> for FisherF<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio + } +} + +/// The Student t distribution, `t(nu)`, where `nu` is the degrees of +/// freedom. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{StudentT, Distribution}; +/// +/// let t = StudentT::new(11.0).unwrap(); +/// let v = t.sample(&mut rand::thread_rng()); +/// println!("{} is from a t(11) distribution", v) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct StudentT<N> { + chi: ChiSquared<N>, + dof: N +} + +impl<N: Float> StudentT<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + /// Create a new Student t distribution with `n` degrees of + /// freedom. + pub fn new(n: N) -> Result<StudentT<N>, ChiSquaredError> { + Ok(StudentT { + chi: ChiSquared::new(n)?, + dof: n + }) + } +} +impl<N: Float> Distribution<N> for StudentT<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + let norm: N = rng.sample(StandardNormal); + norm * (self.dof / self.chi.sample(rng)).sqrt() + } +} + +/// The Beta distribution with shape parameters `alpha` and `beta`. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{Distribution, Beta}; +/// +/// let beta = Beta::new(2.0, 5.0).unwrap(); +/// let v = beta.sample(&mut rand::thread_rng()); +/// println!("{} is from a Beta(2, 5) distribution", v); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Beta<N> { + gamma_a: Gamma<N>, + gamma_b: Gamma<N>, +} + +/// Error type returned from `Beta::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BetaError { + /// `alpha <= 0` or `nan`. + AlphaTooSmall, + /// `beta <= 0` or `nan`. + BetaTooSmall, +} + +impl<N: Float> Beta<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + /// Construct an object representing the `Beta(alpha, beta)` + /// distribution. + pub fn new(alpha: N, beta: N) -> Result<Beta<N>, BetaError> { + Ok(Beta { + gamma_a: Gamma::new(alpha, N::from(1.)) + .map_err(|_| BetaError::AlphaTooSmall)?, + gamma_b: Gamma::new(beta, N::from(1.)) + .map_err(|_| BetaError::BetaTooSmall)?, + }) + } +} + +impl<N: Float> Distribution<N> for Beta<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + let x = self.gamma_a.sample(rng); + let y = self.gamma_b.sample(rng); + x / (x + y) + } +} + +#[cfg(test)] +mod test { + use crate::Distribution; + use super::{Beta, ChiSquared, StudentT, FisherF}; + + #[test] + fn test_chi_squared_one() { + let chi = ChiSquared::new(1.0).unwrap(); + let mut rng = crate::test::rng(201); + for _ in 0..1000 { + chi.sample(&mut rng); + } + } + #[test] + fn test_chi_squared_small() { + let chi = ChiSquared::new(0.5).unwrap(); + let mut rng = crate::test::rng(202); + for _ in 0..1000 { + chi.sample(&mut rng); + } + } + #[test] + fn test_chi_squared_large() { + let chi = ChiSquared::new(30.0).unwrap(); + let mut rng = crate::test::rng(203); + for _ in 0..1000 { + chi.sample(&mut rng); + } + } + #[test] + #[should_panic] + fn test_chi_squared_invalid_dof() { + ChiSquared::new(-1.0).unwrap(); + } + + #[test] + fn test_f() { + let f = FisherF::new(2.0, 32.0).unwrap(); + let mut rng = crate::test::rng(204); + for _ in 0..1000 { + f.sample(&mut rng); + } + } + + #[test] + fn test_t() { + let t = StudentT::new(11.0).unwrap(); + let mut rng = crate::test::rng(205); + for _ in 0..1000 { + t.sample(&mut rng); + } + } + + #[test] + fn test_beta() { + let beta = Beta::new(1.0, 2.0).unwrap(); + let mut rng = crate::test::rng(201); + for _ in 0..1000 { + beta.sample(&mut rng); + } + } + + #[test] + #[should_panic] + fn test_beta_invalid_dof() { + Beta::new(0., 0.).unwrap(); + } +} diff --git a/rand/rand_distr/src/lib.rs b/rand/rand_distr/src/lib.rs new file mode 100644 index 0000000..baf65ed --- /dev/null +++ b/rand/rand_distr/src/lib.rs @@ -0,0 +1,134 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://rust-random.github.io/rand/")] + +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] + +#![allow(clippy::excessive_precision, clippy::float_cmp, clippy::unreadable_literal)] +#![allow(clippy::neg_cmp_op_on_partial_ord)] // suggested fix too verbose + +//! Generating random samples from probability distributions. +//! +//! ## Re-exports +//! +//! This crate is a super-set of the [`rand::distributions`] module. See the +//! [`rand::distributions`] module documentation for an overview of the core +//! [`Distribution`] trait and implementations. +//! +//! The following are re-exported: +//! +//! - The [`Distribution`] trait and [`DistIter`] helper type +//! - The [`Standard`], [`Alphanumeric`], [`Uniform`], [`OpenClosed01`], [`Open01`] and [`Bernoulli`] distributions +//! - The [`weighted`] sub-module +//! +//! ## Distributions +//! +//! This crate provides the following probability distributions: +//! +//! - Related to real-valued quantities that grow linearly +//! (e.g. errors, offsets): +//! - [`Normal`] distribution, and [`StandardNormal`] as a primitive +//! - [`Cauchy`] distribution +//! - Related to Bernoulli trials (yes/no events, with a given probability): +//! - [`Binomial`] distribution +//! - Related to positive real-valued quantities that grow exponentially +//! (e.g. prices, incomes, populations): +//! - [`LogNormal`] distribution +//! - Related to the occurrence of independent events at a given rate: +//! - [`Pareto`] distribution +//! - [`Poisson`] distribution +//! - [`Exp`]onential distribution, and [`Exp1`] as a primitive +//! - [`Weibull`] distribution +//! - Gamma and derived distributions: +//! - [`Gamma`] distribution +//! - [`ChiSquared`] distribution +//! - [`StudentT`] distribution +//! - [`FisherF`] distribution +//! - Triangular distribution: +//! - [`Beta`] distribution +//! - [`Triangular`] distribution +//! - Multivariate probability distributions +//! - [`Dirichlet`] distribution +//! - [`UnitSphere`] distribution +//! - [`UnitBall`] distribution +//! - [`UnitCircle`] distribution +//! - [`UnitDisc`] distribution + +pub use rand::distributions::{Distribution, DistIter, Standard, + Alphanumeric, Uniform, OpenClosed01, Open01, Bernoulli, uniform, weighted}; + +pub use self::unit_sphere::UnitSphere; +pub use self::unit_ball::UnitBall; +pub use self::unit_circle::UnitCircle; +pub use self::unit_disc::UnitDisc; +pub use self::gamma::{Gamma, Error as GammaError, ChiSquared, ChiSquaredError, + FisherF, FisherFError, StudentT, Beta, BetaError}; +pub use self::normal::{Normal, Error as NormalError, LogNormal, StandardNormal}; +pub use self::exponential::{Exp, Error as ExpError, Exp1}; +pub use self::pareto::{Pareto, Error as ParetoError}; +pub use self::pert::{Pert, PertError}; +pub use self::poisson::{Poisson, Error as PoissonError}; +pub use self::binomial::{Binomial, Error as BinomialError}; +pub use self::cauchy::{Cauchy, Error as CauchyError}; +pub use self::dirichlet::{Dirichlet, Error as DirichletError}; +pub use self::triangular::{Triangular, TriangularError}; +pub use self::weibull::{Weibull, Error as WeibullError}; +pub use self::utils::Float; + +mod unit_sphere; +mod unit_ball; +mod unit_circle; +mod unit_disc; +mod gamma; +mod normal; +mod exponential; +mod pareto; +mod pert; +mod poisson; +mod binomial; +mod cauchy; +mod dirichlet; +mod triangular; +mod weibull; +mod utils; +mod ziggurat_tables; + +#[cfg(test)] +mod test { + // Notes on testing + // + // Testing random number distributions correctly is hard. The following + // testing is desired: + // + // - Construction: test initialisation with a few valid parameter sets. + // - Erroneous usage: test that incorrect usage generates an error. + // - Vector: test that usage with fixed inputs (including RNG) generates a + // fixed output sequence on all platforms. + // - Correctness at fixed points (optional): using a specific mock RNG, + // check that specific values are sampled (e.g. end-points and median of + // distribution). + // - Correctness of PDF (extra): generate a histogram of samples within a + // certain range, and check this approximates the PDF. These tests are + // expected to be expensive, and should be behind a feature-gate. + // + // TODO: Vector and correctness tests are largely absent so far. + // NOTE: Some distributions have tests checking only that samples can be + // generated. This is redundant with vector and correctness tests. + + /// Construct a deterministic RNG with the given seed + pub fn rng(seed: u64) -> impl rand::RngCore { + // For tests, we want a statistically good, fast, reproducible RNG. + // PCG32 will do fine, and will be easy to embed if we ever need to. + const INC: u64 = 11634580027462260723; + rand_pcg::Pcg32::new(seed, INC) + } +} diff --git a/rand/rand_distr/src/normal.rs b/rand/rand_distr/src/normal.rs new file mode 100644 index 0000000..882754f --- /dev/null +++ b/rand/rand_distr/src/normal.rs @@ -0,0 +1,219 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2013 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The normal and derived distributions. + +use rand::Rng; +use crate::{ziggurat_tables, Distribution, Open01}; +use crate::utils::{ziggurat, Float}; + +/// Samples floating-point numbers according to the normal distribution +/// `N(0, 1)` (a.k.a. a standard normal, or Gaussian). This is equivalent to +/// `Normal::new(0.0, 1.0)` but faster. +/// +/// See `Normal` for the general normal distribution. +/// +/// Implemented via the ZIGNOR variant[^1] of the Ziggurat method. +/// +/// [^1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to +/// Generate Normal Random Samples*]( +/// https://www.doornik.com/research/ziggurat.pdf). +/// Nuffield College, Oxford +/// +/// # Example +/// ``` +/// use rand::prelude::*; +/// use rand_distr::StandardNormal; +/// +/// let val: f64 = thread_rng().sample(StandardNormal); +/// println!("{}", val); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct StandardNormal; + +impl Distribution<f32> for StandardNormal { + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f32 { + // TODO: use optimal 32-bit implementation + let x: f64 = self.sample(rng); + x as f32 + } +} + +impl Distribution<f64> for StandardNormal { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 { + #[inline] + fn pdf(x: f64) -> f64 { + (-x*x/2.0).exp() + } + #[inline] + fn zero_case<R: Rng + ?Sized>(rng: &mut R, u: f64) -> f64 { + // compute a random number in the tail by hand + + // strange initial conditions, because the loop is not + // do-while, so the condition should be true on the first + // run, they get overwritten anyway (0 < 1, so these are + // good). + let mut x = 1.0f64; + let mut y = 0.0f64; + + while -2.0 * y < x * x { + let x_: f64 = rng.sample(Open01); + let y_: f64 = rng.sample(Open01); + + x = x_.ln() / ziggurat_tables::ZIG_NORM_R; + y = y_.ln(); + } + + if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x } + } + + ziggurat(rng, true, // this is symmetric + &ziggurat_tables::ZIG_NORM_X, + &ziggurat_tables::ZIG_NORM_F, + pdf, zero_case) + } +} + +/// The normal distribution `N(mean, std_dev**2)`. +/// +/// This uses the ZIGNOR variant of the Ziggurat method, see [`StandardNormal`] +/// for more details. +/// +/// Note that [`StandardNormal`] is an optimised implementation for mean 0, and +/// standard deviation 1. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{Normal, Distribution}; +/// +/// // mean 2, standard deviation 3 +/// let normal = Normal::new(2.0, 3.0).unwrap(); +/// let v = normal.sample(&mut rand::thread_rng()); +/// println!("{} is from a N(2, 9) distribution", v) +/// ``` +/// +/// [`StandardNormal`]: crate::StandardNormal +#[derive(Clone, Copy, Debug)] +pub struct Normal<N> { + mean: N, + std_dev: N, +} + +/// Error type returned from `Normal::new` and `LogNormal::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `std_dev < 0` or `nan`. + StdDevTooSmall, +} + +impl<N: Float> Normal<N> +where StandardNormal: Distribution<N> +{ + /// Construct a new `Normal` distribution with the given mean and + /// standard deviation. + #[inline] + pub fn new(mean: N, std_dev: N) -> Result<Normal<N>, Error> { + if !(std_dev >= N::from(0.0)) { + return Err(Error::StdDevTooSmall); + } + Ok(Normal { + mean, + std_dev + }) + } +} + +impl<N: Float> Distribution<N> for Normal<N> +where StandardNormal: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + let n: N = rng.sample(StandardNormal); + self.mean + self.std_dev * n + } +} + + +/// The log-normal distribution `ln N(mean, std_dev**2)`. +/// +/// If `X` is log-normal distributed, then `ln(X)` is `N(mean, std_dev**2)` +/// distributed. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{LogNormal, Distribution}; +/// +/// // mean 2, standard deviation 3 +/// let log_normal = LogNormal::new(2.0, 3.0).unwrap(); +/// let v = log_normal.sample(&mut rand::thread_rng()); +/// println!("{} is from an ln N(2, 9) distribution", v) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct LogNormal<N> { + norm: Normal<N> +} + +impl<N: Float> LogNormal<N> +where StandardNormal: Distribution<N> +{ + /// Construct a new `LogNormal` distribution with the given mean + /// and standard deviation of the logarithm of the distribution. + #[inline] + pub fn new(mean: N, std_dev: N) -> Result<LogNormal<N>, Error> { + if !(std_dev >= N::from(0.0)) { + return Err(Error::StdDevTooSmall); + } + Ok(LogNormal { norm: Normal::new(mean, std_dev).unwrap() }) + } +} + +impl<N: Float> Distribution<N> for LogNormal<N> +where StandardNormal: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + self.norm.sample(rng).exp() + } +} + +#[cfg(test)] +mod tests { + use crate::Distribution; + use super::{Normal, LogNormal}; + + #[test] + fn test_normal() { + let norm = Normal::new(10.0, 10.0).unwrap(); + let mut rng = crate::test::rng(210); + for _ in 0..1000 { + norm.sample(&mut rng); + } + } + #[test] + #[should_panic] + fn test_normal_invalid_sd() { + Normal::new(10.0, -1.0).unwrap(); + } + + + #[test] + fn test_log_normal() { + let lnorm = LogNormal::new(10.0, 10.0).unwrap(); + let mut rng = crate::test::rng(211); + for _ in 0..1000 { + lnorm.sample(&mut rng); + } + } + #[test] + #[should_panic] + fn test_log_normal_invalid_sd() { + LogNormal::new(10.0, -1.0).unwrap(); + } +} diff --git a/rand/rand_distr/src/pareto.rs b/rand/rand_distr/src/pareto.rs new file mode 100644 index 0000000..33ea382 --- /dev/null +++ b/rand/rand_distr/src/pareto.rs @@ -0,0 +1,89 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Pareto distribution. + +use rand::Rng; +use crate::{Distribution, OpenClosed01}; +use crate::utils::Float; + +/// Samples floating-point numbers according to the Pareto distribution +/// +/// # Example +/// ``` +/// use rand::prelude::*; +/// use rand_distr::Pareto; +/// +/// let val: f64 = thread_rng().sample(Pareto::new(1., 2.).unwrap()); +/// println!("{}", val); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Pareto<N> { + scale: N, + inv_neg_shape: N, +} + +/// Error type returned from `Pareto::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `scale <= 0` or `nan`. + ScaleTooSmall, + /// `shape <= 0` or `nan`. + ShapeTooSmall, +} + +impl<N: Float> Pareto<N> +where OpenClosed01: Distribution<N> +{ + /// Construct a new Pareto distribution with given `scale` and `shape`. + /// + /// In the literature, `scale` is commonly written as x<sub>m</sub> or k and + /// `shape` is often written as α. + pub fn new(scale: N, shape: N) -> Result<Pareto<N>, Error> { + if !(scale > N::from(0.0)) { + return Err(Error::ScaleTooSmall); + } + if !(shape > N::from(0.0)) { + return Err(Error::ShapeTooSmall); + } + Ok(Pareto { scale, inv_neg_shape: N::from(-1.0) / shape }) + } +} + +impl<N: Float> Distribution<N> for Pareto<N> +where OpenClosed01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + let u: N = OpenClosed01.sample(rng); + self.scale * u.powf(self.inv_neg_shape) + } +} + +#[cfg(test)] +mod tests { + use crate::Distribution; + use super::Pareto; + + #[test] + #[should_panic] + fn invalid() { + Pareto::new(0., 0.).unwrap(); + } + + #[test] + fn sample() { + let scale = 1.0; + let shape = 2.0; + let d = Pareto::new(scale, shape).unwrap(); + let mut rng = crate::test::rng(1); + for _ in 0..1000 { + let r = d.sample(&mut rng); + assert!(r >= scale); + } + } +} diff --git a/rand/rand_distr/src/pert.rs b/rand/rand_distr/src/pert.rs new file mode 100644 index 0000000..040cd05 --- /dev/null +++ b/rand/rand_distr/src/pert.rs @@ -0,0 +1,132 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! The PERT distribution. + +use rand::Rng; +use crate::{Distribution, Beta, StandardNormal, Exp1, Open01}; +use crate::utils::Float; + +/// The PERT distribution. +/// +/// Similar to the [`Triangular`] distribution, the PERT distribution is +/// parameterised by a range and a mode within that range. Unlike the +/// [`Triangular`] distribution, the probability density function of the PERT +/// distribution is smooth, with a configurable weighting around the mode. +/// +/// # Example +/// +/// ```rust +/// use rand_distr::{Pert, Distribution}; +/// +/// let d = Pert::new(0., 5., 2.5).unwrap(); +/// let v = d.sample(&mut rand::thread_rng()); +/// println!("{} is from a PERT distribution", v); +/// ``` +/// +/// [`Triangular`]: crate::Triangular +#[derive(Clone, Copy, Debug)] +pub struct Pert<N> { + min: N, + range: N, + beta: Beta<N>, +} + +/// Error type returned from [`Pert`] constructors. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PertError { + /// `max < min` or `min` or `max` is NaN. + RangeTooSmall, + /// `mode < min` or `mode > max` or `mode` is NaN. + ModeRange, + /// `shape < 0` or `shape` is NaN + ShapeTooSmall, +} + +impl<N: Float> Pert<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + /// Set up the PERT distribution with defined `min`, `max` and `mode`. + /// + /// This is equivalent to calling `Pert::new_shape` with `shape == 4.0`. + #[inline] + pub fn new(min: N, max: N, mode: N) -> Result<Pert<N>, PertError> { + Pert::new_with_shape(min, max, mode, N::from(4.)) + } + + /// Set up the PERT distribution with defined `min`, `max`, `mode` and + /// `shape`. + pub fn new_with_shape(min: N, max: N, mode: N, shape: N) -> Result<Pert<N>, PertError> { + if !(max > min) { + return Err(PertError::RangeTooSmall); + } + if !(mode >= min && max >= mode) { + return Err(PertError::ModeRange); + } + if !(shape >= N::from(0.)) { + return Err(PertError::ShapeTooSmall); + } + + let range = max - min; + let mu = (min + max + shape * mode) / (shape + N::from(2.)); + let v = if mu == mode { + shape * N::from(0.5) + N::from(1.) + } else { + (mu - min) * (N::from(2.) * mode - min - max) + / ((mode - mu) * (max - min)) + }; + let w = v * (max - mu) / (mu - min); + let beta = Beta::new(v, w).map_err(|_| PertError::RangeTooSmall)?; + Ok(Pert{ min, range, beta }) + } +} + +impl<N: Float> Distribution<N> for Pert<N> +where StandardNormal: Distribution<N>, Exp1: Distribution<N>, Open01: Distribution<N> +{ + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + self.beta.sample(rng) * self.range + self.min + } +} + +#[cfg(test)] +mod test { + use std::f64; + use super::*; + + #[test] + fn test_pert() { + for &(min, max, mode) in &[ + (-1., 1., 0.), + (1., 2., 1.), + (5., 25., 25.), + ] { + let _distr = Pert::new(min, max, mode).unwrap(); + // TODO: test correctness + } + + for &(min, max, mode) in &[ + (-1., 1., 2.), + (-1., 1., -2.), + (2., 1., 1.), + ] { + assert!(Pert::new(min, max, mode).is_err()); + } + } + + #[test] + fn value_stability() { + let rng = crate::test::rng(860); + let distr = Pert::new(2., 10., 3.).unwrap(); // mean = 4, var = 12/7 + let seq = distr.sample_iter(rng).take(5).collect::<Vec<f64>>(); + println!("seq: {:?}", seq); + let expected = vec![4.631484136029422, 3.307201472321789, + 3.29995019556348, 3.66835483991721, 3.514246139933899]; + assert!(seq == expected); + } +} diff --git a/rand/rand_distr/src/poisson.rs b/rand/rand_distr/src/poisson.rs new file mode 100644 index 0000000..4f4a0b7 --- /dev/null +++ b/rand/rand_distr/src/poisson.rs @@ -0,0 +1,233 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2016-2017 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Poisson distribution. + +use rand::Rng; +use crate::{Distribution, Cauchy, Standard}; +use crate::utils::Float; + +/// The Poisson distribution `Poisson(lambda)`. +/// +/// This distribution has a density function: +/// `f(k) = lambda^k * exp(-lambda) / k!` for `k >= 0`. +/// +/// # Example +/// +/// ``` +/// use rand_distr::{Poisson, Distribution}; +/// +/// let poi = Poisson::new(2.0).unwrap(); +/// let v: u64 = poi.sample(&mut rand::thread_rng()); +/// println!("{} is from a Poisson(2) distribution", v); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Poisson<N> { + lambda: N, + // precalculated values + exp_lambda: N, + log_lambda: N, + sqrt_2lambda: N, + magic_val: N, +} + +/// Error type returned from `Poisson::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `lambda <= 0` or `nan`. + ShapeTooSmall, +} + +impl<N: Float> Poisson<N> +where Standard: Distribution<N> +{ + /// Construct a new `Poisson` with the given shape parameter + /// `lambda`. + pub fn new(lambda: N) -> Result<Poisson<N>, Error> { + if !(lambda > N::from(0.0)) { + return Err(Error::ShapeTooSmall); + } + let log_lambda = lambda.ln(); + Ok(Poisson { + lambda, + exp_lambda: (-lambda).exp(), + log_lambda, + sqrt_2lambda: (N::from(2.0) * lambda).sqrt(), + magic_val: lambda * log_lambda - (N::from(1.0) + lambda).log_gamma(), + }) + } +} + +impl<N: Float> Distribution<N> for Poisson<N> +where Standard: Distribution<N> +{ + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + // using the algorithm from Numerical Recipes in C + + // for low expected values use the Knuth method + if self.lambda < N::from(12.0) { + let mut result = N::from(0.); + let mut p = N::from(1.0); + while p > self.exp_lambda { + p *= rng.gen::<N>(); + result += N::from(1.); + } + result - N::from(1.) + } + // high expected values - rejection method + else { + // we use the Cauchy distribution as the comparison distribution + // f(x) ~ 1/(1+x^2) + let cauchy = Cauchy::new(N::from(0.0), N::from(1.0)).unwrap(); + let mut result; + + loop { + let mut comp_dev; + + loop { + // draw from the Cauchy distribution + comp_dev = rng.sample(cauchy); + // shift the peak of the comparison ditribution + result = self.sqrt_2lambda * comp_dev + self.lambda; + // repeat the drawing until we are in the range of possible values + if result >= N::from(0.0) { + break; + } + } + // now the result is a random variable greater than 0 with Cauchy distribution + // the result should be an integer value + result = result.floor(); + + // this is the ratio of the Poisson distribution to the comparison distribution + // the magic value scales the distribution function to a range of approximately 0-1 + // since it is not exact, we multiply the ratio by 0.9 to avoid ratios greater than 1 + // this doesn't change the resulting distribution, only increases the rate of failed drawings + let check = N::from(0.9) * (N::from(1.0) + comp_dev * comp_dev) + * (result * self.log_lambda - (N::from(1.0) + result).log_gamma() - self.magic_val).exp(); + + // check with uniform random value - if below the threshold, we are within the target distribution + if rng.gen::<N>() <= check { + break; + } + } + result + } + } +} + +impl<N: Float> Distribution<u64> for Poisson<N> +where Standard: Distribution<N> +{ + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 { + let result: N = self.sample(rng); + result.to_u64().unwrap() + } +} + +#[cfg(test)] +mod test { + use crate::Distribution; + use super::Poisson; + + #[test] + fn test_poisson_10() { + let poisson = Poisson::new(10.0).unwrap(); + let mut rng = crate::test::rng(123); + let mut sum_u64 = 0; + let mut sum_f64 = 0.; + for _ in 0..1000 { + let s_u64: u64 = poisson.sample(&mut rng); + let s_f64: f64 = poisson.sample(&mut rng); + sum_u64 += s_u64; + sum_f64 += s_f64; + } + let avg_u64 = (sum_u64 as f64) / 1000.0; + let avg_f64 = sum_f64 / 1000.0; + println!("Poisson averages: {} (u64) {} (f64)", avg_u64, avg_f64); + for &avg in &[avg_u64, avg_f64] { + assert!((avg - 10.0).abs() < 0.5); // not 100% certain, but probable enough + } + } + + #[test] + fn test_poisson_15() { + // Take the 'high expected values' path + let poisson = Poisson::new(15.0).unwrap(); + let mut rng = crate::test::rng(123); + let mut sum_u64 = 0; + let mut sum_f64 = 0.; + for _ in 0..1000 { + let s_u64: u64 = poisson.sample(&mut rng); + let s_f64: f64 = poisson.sample(&mut rng); + sum_u64 += s_u64; + sum_f64 += s_f64; + } + let avg_u64 = (sum_u64 as f64) / 1000.0; + let avg_f64 = sum_f64 / 1000.0; + println!("Poisson average: {} (u64) {} (f64)", avg_u64, avg_f64); + for &avg in &[avg_u64, avg_f64] { + assert!((avg - 15.0).abs() < 0.5); // not 100% certain, but probable enough + } + } + + #[test] + fn test_poisson_10_f32() { + let poisson = Poisson::new(10.0f32).unwrap(); + let mut rng = crate::test::rng(123); + let mut sum_u64 = 0; + let mut sum_f32 = 0.; + for _ in 0..1000 { + let s_u64: u64 = poisson.sample(&mut rng); + let s_f32: f32 = poisson.sample(&mut rng); + sum_u64 += s_u64; + sum_f32 += s_f32; + } + let avg_u64 = (sum_u64 as f32) / 1000.0; + let avg_f32 = sum_f32 / 1000.0; + println!("Poisson averages: {} (u64) {} (f32)", avg_u64, avg_f32); + for &avg in &[avg_u64, avg_f32] { + assert!((avg - 10.0).abs() < 0.5); // not 100% certain, but probable enough + } + } + + #[test] + fn test_poisson_15_f32() { + // Take the 'high expected values' path + let poisson = Poisson::new(15.0f32).unwrap(); + let mut rng = crate::test::rng(123); + let mut sum_u64 = 0; + let mut sum_f32 = 0.; + for _ in 0..1000 { + let s_u64: u64 = poisson.sample(&mut rng); + let s_f32: f32 = poisson.sample(&mut rng); + sum_u64 += s_u64; + sum_f32 += s_f32; + } + let avg_u64 = (sum_u64 as f32) / 1000.0; + let avg_f32 = sum_f32 / 1000.0; + println!("Poisson average: {} (u64) {} (f32)", avg_u64, avg_f32); + for &avg in &[avg_u64, avg_f32] { + assert!((avg - 15.0).abs() < 0.5); // not 100% certain, but probable enough + } + } + + #[test] + #[should_panic] + fn test_poisson_invalid_lambda_zero() { + Poisson::new(0.0).unwrap(); + } + + #[test] + #[should_panic] + fn test_poisson_invalid_lambda_neg() { + Poisson::new(-10.0).unwrap(); + } +} diff --git a/rand/rand_distr/src/triangular.rs b/rand/rand_distr/src/triangular.rs new file mode 100644 index 0000000..dd0bbfb --- /dev/null +++ b/rand/rand_distr/src/triangular.rs @@ -0,0 +1,125 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! The triangular distribution. + +use rand::Rng; +use crate::{Distribution, Standard}; +use crate::utils::Float; + +/// The triangular distribution. +/// +/// A continuous probability distribution parameterised by a range, and a mode +/// (most likely value) within that range. +/// +/// The probability density function is triangular. For a similar distribution +/// with a smooth PDF, see the [`Pert`] distribution. +/// +/// # Example +/// +/// ```rust +/// use rand_distr::{Triangular, Distribution}; +/// +/// let d = Triangular::new(0., 5., 2.5).unwrap(); +/// let v = d.sample(&mut rand::thread_rng()); +/// println!("{} is from a triangular distribution", v); +/// ``` +/// +/// [`Pert`]: crate::Pert +#[derive(Clone, Copy, Debug)] +pub struct Triangular<N> { + min: N, + max: N, + mode: N, +} + +/// Error type returned from [`Triangular::new`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TriangularError { + /// `max < min` or `min` or `max` is NaN. + RangeTooSmall, + /// `mode < min` or `mode > max` or `mode` is NaN. + ModeRange, +} + +impl<N: Float> Triangular<N> +where Standard: Distribution<N> +{ + /// Set up the Triangular distribution with defined `min`, `max` and `mode`. + #[inline] + pub fn new(min: N, max: N, mode: N) -> Result<Triangular<N>, TriangularError> { + if !(max >= min) { + return Err(TriangularError::RangeTooSmall); + } + if !(mode >= min && max >= mode) { + return Err(TriangularError::ModeRange); + } + Ok(Triangular { min, max, mode }) + } +} + +impl<N: Float> Distribution<N> for Triangular<N> +where Standard: Distribution<N> +{ + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + let f: N = rng.sample(Standard); + let diff_mode_min = self.mode - self.min; + let range = self.max - self.min; + let f_range = f * range; + if f_range < diff_mode_min { + self.min + (f_range * diff_mode_min).sqrt() + } else { + self.max - ((range - f_range) * (self.max - self.mode)).sqrt() + } + } +} + +#[cfg(test)] +mod test { + use std::f64; + use rand::{Rng, rngs::mock}; + use super::*; + + #[test] + fn test_triangular() { + let mut half_rng = mock::StepRng::new(0x8000_0000_0000_0000, 0); + assert_eq!(half_rng.gen::<f64>(), 0.5); + for &(min, max, mode, median) in &[ + (-1., 1., 0., 0.), + (1., 2., 1., 2. - 0.5f64.sqrt()), + (5., 25., 25., 5. + 200f64.sqrt()), + (1e-5, 1e5, 1e-3, 1e5 - 4999999949.5f64.sqrt()), + (0., 1., 0.9, 0.45f64.sqrt()), + (-4., -0.5, -2., -4.0 + 3.5f64.sqrt()), + ] { + println!("{} {} {} {}", min, max, mode, median); + let distr = Triangular::new(min, max, mode).unwrap(); + // Test correct value at median: + assert_eq!(distr.sample(&mut half_rng), median); + } + + for &(min, max, mode) in &[ + (-1., 1., 2.), + (-1., 1., -2.), + (2., 1., 1.), + ] { + assert!(Triangular::new(min, max, mode).is_err()); + } + } + + #[test] + fn value_stability() { + let rng = crate::test::rng(860); + let distr = Triangular::new(2., 10., 3.).unwrap(); + let seq = distr.sample_iter(rng).take(5).collect::<Vec<f64>>(); + println!("seq: {:?}", seq); + let expected = vec![5.74373257511361, 7.890059162791258, + 4.7256280652553455, 2.9474808121184077, 3.058301946314053]; + assert!(seq == expected); + } +} diff --git a/rand/rand_distr/src/unit_ball.rs b/rand/rand_distr/src/unit_ball.rs new file mode 100644 index 0000000..9d61627 --- /dev/null +++ b/rand/rand_distr/src/unit_ball.rs @@ -0,0 +1,69 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rand::Rng; +use crate::{Distribution, Uniform, uniform::SampleUniform}; +use crate::utils::Float; + +/// Samples uniformly from the unit ball (surface and interior) in three +/// dimensions. +/// +/// Implemented via rejection sampling. +/// +/// +/// # Example +/// +/// ``` +/// use rand_distr::{UnitBall, Distribution}; +/// +/// let v: [f64; 3] = UnitBall.sample(&mut rand::thread_rng()); +/// println!("{:?} is from the unit ball.", v) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct UnitBall; + +impl<N: Float + SampleUniform> Distribution<[N; 3]> for UnitBall { + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [N; 3] { + let uniform = Uniform::new(N::from(-1.), N::from(1.)); + let mut x1; + let mut x2; + let mut x3; + loop { + x1 = uniform.sample(rng); + x2 = uniform.sample(rng); + x3 = uniform.sample(rng); + if x1*x1 + x2*x2 + x3*x3 <= N::from(1.) { + break; + } + } + [x1, x2, x3] + } +} + +#[cfg(test)] +mod tests { + use crate::Distribution; + use super::UnitBall; + + #[test] + fn value_stability() { + let mut rng = crate::test::rng(2); + let expected = [ + [0.018035709265959987, -0.4348771383120438, -0.07982762085055706], + [0.10588569388223945, -0.4734350111375454, -0.7392104908825501], + [0.11060237642041049, -0.16065642822852677, -0.8444043930440075] + ]; + let samples: [[f64; 3]; 3] = [ + UnitBall.sample(&mut rng), + UnitBall.sample(&mut rng), + UnitBall.sample(&mut rng), + ]; + assert_eq!(samples, expected); + } +} diff --git a/rand/rand_distr/src/unit_circle.rs b/rand/rand_distr/src/unit_circle.rs new file mode 100644 index 0000000..5863a1a --- /dev/null +++ b/rand/rand_distr/src/unit_circle.rs @@ -0,0 +1,99 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rand::Rng; +use crate::{Distribution, Uniform, uniform::SampleUniform}; +use crate::utils::Float; + +/// Samples uniformly from the edge of the unit circle in two dimensions. +/// +/// Implemented via a method by von Neumann[^1]. +/// +/// +/// # Example +/// +/// ``` +/// use rand_distr::{UnitCircle, Distribution}; +/// +/// let v: [f64; 2] = UnitCircle.sample(&mut rand::thread_rng()); +/// println!("{:?} is from the unit circle.", v) +/// ``` +/// +/// [^1]: von Neumann, J. (1951) [*Various Techniques Used in Connection with +/// Random Digits.*](https://mcnp.lanl.gov/pdf_files/nbs_vonneumann.pdf) +/// NBS Appl. Math. Ser., No. 12. Washington, DC: U.S. Government Printing +/// Office, pp. 36-38. +#[derive(Clone, Copy, Debug)] +pub struct UnitCircle; + +impl<N: Float + SampleUniform> Distribution<[N; 2]> for UnitCircle { + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [N; 2] { + let uniform = Uniform::new(N::from(-1.), N::from(1.)); + let mut x1; + let mut x2; + let mut sum; + loop { + x1 = uniform.sample(rng); + x2 = uniform.sample(rng); + sum = x1*x1 + x2*x2; + if sum < N::from(1.) { + break; + } + } + let diff = x1*x1 - x2*x2; + [diff / sum, N::from(2.)*x1*x2 / sum] + } +} + +#[cfg(test)] +mod tests { + use crate::Distribution; + use super::UnitCircle; + + /// Assert that two numbers are almost equal to each other. + /// + /// On panic, this macro will print the values of the expressions with their + /// debug representations. + macro_rules! assert_almost_eq { + ($a:expr, $b:expr, $prec:expr) => ( + let diff = ($a - $b).abs(); + if diff > $prec { + panic!(format!( + "assertion failed: `abs(left - right) = {:.1e} < {:e}`, \ + (left: `{}`, right: `{}`)", + diff, $prec, $a, $b)); + } + ); + } + + #[test] + fn norm() { + let mut rng = crate::test::rng(1); + for _ in 0..1000 { + let x: [f64; 2] = UnitCircle.sample(&mut rng); + assert_almost_eq!(x[0]*x[0] + x[1]*x[1], 1., 1e-15); + } + } + + #[test] + fn value_stability() { + let mut rng = crate::test::rng(2); + let expected = [ + [-0.9965658683520504, -0.08280380447614634], + [-0.9790853270389644, -0.20345004884984505], + [-0.8449189758898707, 0.5348943112253227], + ]; + let samples: [[f64; 2]; 3] = [ + UnitCircle.sample(&mut rng), + UnitCircle.sample(&mut rng), + UnitCircle.sample(&mut rng), + ]; + assert_eq!(samples, expected); + } +} diff --git a/rand/rand_distr/src/unit_disc.rs b/rand/rand_distr/src/unit_disc.rs new file mode 100644 index 0000000..97abc2f --- /dev/null +++ b/rand/rand_distr/src/unit_disc.rs @@ -0,0 +1,66 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rand::Rng; +use crate::{Distribution, Uniform, uniform::SampleUniform}; +use crate::utils::Float; + +/// Samples uniformly from the unit disc in two dimensions. +/// +/// Implemented via rejection sampling. +/// +/// +/// # Example +/// +/// ``` +/// use rand_distr::{UnitDisc, Distribution}; +/// +/// let v: [f64; 2] = UnitDisc.sample(&mut rand::thread_rng()); +/// println!("{:?} is from the unit Disc.", v) +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct UnitDisc; + +impl<N: Float + SampleUniform> Distribution<[N; 2]> for UnitDisc { + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [N; 2] { + let uniform = Uniform::new(N::from(-1.), N::from(1.)); + let mut x1; + let mut x2; + loop { + x1 = uniform.sample(rng); + x2 = uniform.sample(rng); + if x1*x1 + x2*x2 <= N::from(1.) { + break; + } + } + [x1, x2] + } +} + +#[cfg(test)] +mod tests { + use crate::Distribution; + use super::UnitDisc; + + #[test] + fn value_stability() { + let mut rng = crate::test::rng(2); + let expected = [ + [0.018035709265959987, -0.4348771383120438], + [-0.07982762085055706, 0.7765329819820659], + [0.21450745997299503, 0.7398636984333291] + ]; + let samples: [[f64; 2]; 3] = [ + UnitDisc.sample(&mut rng), + UnitDisc.sample(&mut rng), + UnitDisc.sample(&mut rng), + ]; + assert_eq!(samples, expected); + } +} diff --git a/rand/rand_distr/src/unit_sphere.rs b/rand/rand_distr/src/unit_sphere.rs new file mode 100644 index 0000000..8e0c361 --- /dev/null +++ b/rand/rand_distr/src/unit_sphere.rs @@ -0,0 +1,94 @@ +// Copyright 2018-2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rand::Rng; +use crate::{Distribution, Uniform, uniform::SampleUniform}; +use crate::utils::Float; + +/// Samples uniformly from the surface of the unit sphere in three dimensions. +/// +/// Implemented via a method by Marsaglia[^1]. +/// +/// +/// # Example +/// +/// ``` +/// use rand_distr::{UnitSphere, Distribution}; +/// +/// let v: [f64; 3] = UnitSphere.sample(&mut rand::thread_rng()); +/// println!("{:?} is from the unit sphere surface.", v) +/// ``` +/// +/// [^1]: Marsaglia, George (1972). [*Choosing a Point from the Surface of a +/// Sphere.*](https://doi.org/10.1214/aoms/1177692644) +/// Ann. Math. Statist. 43, no. 2, 645--646. +#[derive(Clone, Copy, Debug)] +pub struct UnitSphere; + +impl<N: Float + SampleUniform> Distribution<[N; 3]> for UnitSphere { + #[inline] + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [N; 3] { + let uniform = Uniform::new(N::from(-1.), N::from(1.)); + loop { + let (x1, x2) = (uniform.sample(rng), uniform.sample(rng)); + let sum = x1*x1 + x2*x2; + if sum >= N::from(1.) { + continue; + } + let factor = N::from(2.) * (N::from(1.0) - sum).sqrt(); + return [x1 * factor, x2 * factor, N::from(1.) - N::from(2.)*sum]; + } + } +} + +#[cfg(test)] +mod tests { + use crate::Distribution; + use super::UnitSphere; + + /// Assert that two numbers are almost equal to each other. + /// + /// On panic, this macro will print the values of the expressions with their + /// debug representations. + macro_rules! assert_almost_eq { + ($a:expr, $b:expr, $prec:expr) => ( + let diff = ($a - $b).abs(); + if diff > $prec { + panic!(format!( + "assertion failed: `abs(left - right) = {:.1e} < {:e}`, \ + (left: `{}`, right: `{}`)", + diff, $prec, $a, $b)); + } + ); + } + + #[test] + fn norm() { + let mut rng = crate::test::rng(1); + for _ in 0..1000 { + let x: [f64; 3] = UnitSphere.sample(&mut rng); + assert_almost_eq!(x[0]*x[0] + x[1]*x[1] + x[2]*x[2], 1., 1e-15); + } + } + + #[test] + fn value_stability() { + let mut rng = crate::test::rng(2); + let expected = [ + [0.03247542860231647, -0.7830477442152738, 0.6211131755296027], + [-0.09978440840914075, 0.9706650829833128, -0.21875184231323952], + [0.2735582468624679, 0.9435374242279655, -0.1868234852870203], + ]; + let samples: [[f64; 3]; 3] = [ + UnitSphere.sample(&mut rng), + UnitSphere.sample(&mut rng), + UnitSphere.sample(&mut rng), + ]; + assert_eq!(samples, expected); + } +} diff --git a/rand/rand_distr/src/utils.rs b/rand/rand_distr/src/utils.rs new file mode 100644 index 0000000..75b3500 --- /dev/null +++ b/rand/rand_distr/src/utils.rs @@ -0,0 +1,234 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Math helper functions + +use rand::Rng; +use crate::ziggurat_tables; +use rand::distributions::hidden_export::IntoFloat; +use core::{cmp, ops}; + +/// Trait for floating-point scalar types +/// +/// This allows many distributions to work with `f32` or `f64` parameters and is +/// potentially extensible. Note however that the `Exp1` and `StandardNormal` +/// distributions are implemented exclusively for `f32` and `f64`. +/// +/// The bounds and methods are based purely on internal +/// requirements, and will change as needed. +pub trait Float: Copy + Sized + cmp::PartialOrd + + ops::Neg<Output = Self> + + ops::Add<Output = Self> + + ops::Sub<Output = Self> + + ops::Mul<Output = Self> + + ops::Div<Output = Self> + + ops::AddAssign + ops::SubAssign + ops::MulAssign + ops::DivAssign +{ + /// The constant π + fn pi() -> Self; + /// Support approximate representation of a f64 value + fn from(x: f64) -> Self; + /// Support converting to an unsigned integer. + fn to_u64(self) -> Option<u64>; + + /// Take the absolute value of self + fn abs(self) -> Self; + /// Take the largest integer less than or equal to self + fn floor(self) -> Self; + + /// Take the exponential of self + fn exp(self) -> Self; + /// Take the natural logarithm of self + fn ln(self) -> Self; + /// Take square root of self + fn sqrt(self) -> Self; + /// Take self to a floating-point power + fn powf(self, power: Self) -> Self; + + /// Take the tangent of self + fn tan(self) -> Self; + /// Take the logarithm of the gamma function of self + fn log_gamma(self) -> Self; +} + +impl Float for f32 { + #[inline] + fn pi() -> Self { core::f32::consts::PI } + #[inline] + fn from(x: f64) -> Self { x as f32 } + #[inline] + fn to_u64(self) -> Option<u64> { + if self >= 0. && self <= ::core::u64::MAX as f32 { + Some(self as u64) + } else { + None + } + } + + #[inline] + fn abs(self) -> Self { self.abs() } + #[inline] + fn floor(self) -> Self { self.floor() } + + #[inline] + fn exp(self) -> Self { self.exp() } + #[inline] + fn ln(self) -> Self { self.ln() } + #[inline] + fn sqrt(self) -> Self { self.sqrt() } + #[inline] + fn powf(self, power: Self) -> Self { self.powf(power) } + + #[inline] + fn tan(self) -> Self { self.tan() } + #[inline] + fn log_gamma(self) -> Self { + let result = log_gamma(self.into()); + assert!(result <= ::core::f32::MAX.into()); + assert!(result >= ::core::f32::MIN.into()); + result as f32 + } +} + +impl Float for f64 { + #[inline] + fn pi() -> Self { core::f64::consts::PI } + #[inline] + fn from(x: f64) -> Self { x } + #[inline] + fn to_u64(self) -> Option<u64> { + if self >= 0. && self <= ::core::u64::MAX as f64 { + Some(self as u64) + } else { + None + } + } + + #[inline] + fn abs(self) -> Self { self.abs() } + #[inline] + fn floor(self) -> Self { self.floor() } + + #[inline] + fn exp(self) -> Self { self.exp() } + #[inline] + fn ln(self) -> Self { self.ln() } + #[inline] + fn sqrt(self) -> Self { self.sqrt() } + #[inline] + fn powf(self, power: Self) -> Self { self.powf(power) } + + #[inline] + fn tan(self) -> Self { self.tan() } + #[inline] + fn log_gamma(self) -> Self { log_gamma(self) } +} + +/// Calculates ln(gamma(x)) (natural logarithm of the gamma +/// function) using the Lanczos approximation. +/// +/// The approximation expresses the gamma function as: +/// `gamma(z+1) = sqrt(2*pi)*(z+g+0.5)^(z+0.5)*exp(-z-g-0.5)*Ag(z)` +/// `g` is an arbitrary constant; we use the approximation with `g=5`. +/// +/// Noting that `gamma(z+1) = z*gamma(z)` and applying `ln` to both sides: +/// `ln(gamma(z)) = (z+0.5)*ln(z+g+0.5)-(z+g+0.5) + ln(sqrt(2*pi)*Ag(z)/z)` +/// +/// `Ag(z)` is an infinite series with coefficients that can be calculated +/// ahead of time - we use just the first 6 terms, which is good enough +/// for most purposes. +pub(crate) fn log_gamma(x: f64) -> f64 { + // precalculated 6 coefficients for the first 6 terms of the series + let coefficients: [f64; 6] = [ + 76.18009172947146, + -86.50532032941677, + 24.01409824083091, + -1.231739572450155, + 0.1208650973866179e-2, + -0.5395239384953e-5, + ]; + + // (x+0.5)*ln(x+g+0.5)-(x+g+0.5) + let tmp = x + 5.5; + let log = (x + 0.5) * tmp.ln() - tmp; + + // the first few terms of the series for Ag(x) + let mut a = 1.000000000190015; + let mut denom = x; + for &coeff in &coefficients { + denom += 1.0; + a += coeff / denom; + } + + // get everything together + // a is Ag(x) + // 2.5066... is sqrt(2pi) + log + (2.5066282746310005 * a / x).ln() +} + +/// Sample a random number using the Ziggurat method (specifically the +/// ZIGNOR variant from Doornik 2005). Most of the arguments are +/// directly from the paper: +/// +/// * `rng`: source of randomness +/// * `symmetric`: whether this is a symmetric distribution, or one-sided with P(x < 0) = 0. +/// * `X`: the $x_i$ abscissae. +/// * `F`: precomputed values of the PDF at the $x_i$, (i.e. $f(x_i)$) +/// * `F_DIFF`: precomputed values of $f(x_i) - f(x_{i+1})$ +/// * `pdf`: the probability density function +/// * `zero_case`: manual sampling from the tail when we chose the +/// bottom box (i.e. i == 0) + +// the perf improvement (25-50%) is definitely worth the extra code +// size from force-inlining. +#[inline(always)] +pub(crate) fn ziggurat<R: Rng + ?Sized, P, Z>( + rng: &mut R, + symmetric: bool, + x_tab: ziggurat_tables::ZigTable, + f_tab: ziggurat_tables::ZigTable, + mut pdf: P, + mut zero_case: Z) + -> f64 where P: FnMut(f64) -> f64, Z: FnMut(&mut R, f64) -> f64 { + loop { + // As an optimisation we re-implement the conversion to a f64. + // From the remaining 12 most significant bits we use 8 to construct `i`. + // This saves us generating a whole extra random number, while the added + // precision of using 64 bits for f64 does not buy us much. + let bits = rng.next_u64(); + let i = bits as usize & 0xff; + + let u = if symmetric { + // Convert to a value in the range [2,4) and substract to get [-1,1) + // We can't convert to an open range directly, that would require + // substracting `3.0 - EPSILON`, which is not representable. + // It is possible with an extra step, but an open range does not + // seem neccesary for the ziggurat algorithm anyway. + (bits >> 12).into_float_with_exponent(1) - 3.0 + } else { + // Convert to a value in the range [1,2) and substract to get (0,1) + (bits >> 12).into_float_with_exponent(0) + - (1.0 - std::f64::EPSILON / 2.0) + }; + let x = u * x_tab[i]; + + let test_x = if symmetric { x.abs() } else {x}; + + // algebraically equivalent to |u| < x_tab[i+1]/x_tab[i] (or u < x_tab[i+1]/x_tab[i]) + if test_x < x_tab[i + 1] { + return x; + } + if i == 0 { + return zero_case(rng, u); + } + // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 + if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen::<f64>() < pdf(x) { + return x; + } + } +} diff --git a/rand/rand_distr/src/weibull.rs b/rand/rand_distr/src/weibull.rs new file mode 100644 index 0000000..ddde380 --- /dev/null +++ b/rand/rand_distr/src/weibull.rs @@ -0,0 +1,86 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Weibull distribution. + +use rand::Rng; +use crate::{Distribution, OpenClosed01}; +use crate::utils::Float; + +/// Samples floating-point numbers according to the Weibull distribution +/// +/// # Example +/// ``` +/// use rand::prelude::*; +/// use rand_distr::Weibull; +/// +/// let val: f64 = thread_rng().sample(Weibull::new(1., 10.).unwrap()); +/// println!("{}", val); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct Weibull<N> { + inv_shape: N, + scale: N, +} + +/// Error type returned from `Weibull::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// `scale <= 0` or `nan`. + ScaleTooSmall, + /// `shape <= 0` or `nan`. + ShapeTooSmall, +} + +impl<N: Float> Weibull<N> +where OpenClosed01: Distribution<N> +{ + /// Construct a new `Weibull` distribution with given `scale` and `shape`. + pub fn new(scale: N, shape: N) -> Result<Weibull<N>, Error> { + if !(scale > N::from(0.0)) { + return Err(Error::ScaleTooSmall); + } + if !(shape > N::from(0.0)) { + return Err(Error::ShapeTooSmall); + } + Ok(Weibull { inv_shape: N::from(1.)/shape, scale }) + } +} + +impl<N: Float> Distribution<N> for Weibull<N> +where OpenClosed01: Distribution<N> +{ + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> N { + let x: N = rng.sample(OpenClosed01); + self.scale * (-x.ln()).powf(self.inv_shape) + } +} + +#[cfg(test)] +mod tests { + use crate::Distribution; + use super::Weibull; + + #[test] + #[should_panic] + fn invalid() { + Weibull::new(0., 0.).unwrap(); + } + + #[test] + fn sample() { + let scale = 1.0; + let shape = 2.0; + let d = Weibull::new(scale, shape).unwrap(); + let mut rng = crate::test::rng(1); + for _ in 0..1000 { + let r = d.sample(&mut rng); + assert!(r >= 0.); + } + } +} diff --git a/rand/rand_distr/src/ziggurat_tables.rs b/rand/rand_distr/src/ziggurat_tables.rs new file mode 100644 index 0000000..ca1ce30 --- /dev/null +++ b/rand/rand_distr/src/ziggurat_tables.rs @@ -0,0 +1,279 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2013 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tables for distributions which are sampled using the ziggurat +// algorithm. Autogenerated by `ziggurat_tables.py`. + +pub type ZigTable = &'static [f64; 257]; +pub const ZIG_NORM_R: f64 = 3.654152885361008796; +pub static ZIG_NORM_X: [f64; 257] = + [3.910757959537090045, 3.654152885361008796, 3.449278298560964462, 3.320244733839166074, + 3.224575052047029100, 3.147889289517149969, 3.083526132001233044, 3.027837791768635434, + 2.978603279880844834, 2.934366867207854224, 2.894121053612348060, 2.857138730872132548, + 2.822877396825325125, 2.790921174000785765, 2.760944005278822555, 2.732685359042827056, + 2.705933656121858100, 2.680514643284522158, 2.656283037575502437, 2.633116393630324570, + 2.610910518487548515, 2.589575986706995181, 2.569035452680536569, 2.549221550323460761, + 2.530075232158516929, 2.511544441625342294, 2.493583041269680667, 2.476149939669143318, + 2.459208374333311298, 2.442725318198956774, 2.426670984935725972, 2.411018413899685520, + 2.395743119780480601, 2.380822795170626005, 2.366237056715818632, 2.351967227377659952, + 2.337996148795031370, 2.324308018869623016, 2.310888250599850036, 2.297723348901329565, + 2.284800802722946056, 2.272108990226823888, 2.259637095172217780, 2.247375032945807760, + 2.235313384928327984, 2.223443340090905718, 2.211756642882544366, 2.200245546609647995, + 2.188902771624720689, 2.177721467738641614, 2.166695180352645966, 2.155817819875063268, + 2.145083634046203613, 2.134487182844320152, 2.124023315687815661, 2.113687150684933957, + 2.103474055713146829, 2.093379631137050279, 2.083399693996551783, 2.073530263516978778, + 2.063767547809956415, 2.054107931648864849, 2.044547965215732788, 2.035084353727808715, + 2.025713947862032960, 2.016433734904371722, 2.007240830558684852, 1.998132471356564244, + 1.989106007615571325, 1.980158896898598364, 1.971288697931769640, 1.962493064942461896, + 1.953769742382734043, 1.945116560006753925, 1.936531428273758904, 1.928012334050718257, + 1.919557336591228847, 1.911164563769282232, 1.902832208548446369, 1.894558525668710081, + 1.886341828534776388, 1.878180486290977669, 1.870072921069236838, 1.862017605397632281, + 1.854013059758148119, 1.846057850283119750, 1.838150586580728607, 1.830289919680666566, + 1.822474540091783224, 1.814703175964167636, 1.806974591348693426, 1.799287584547580199, + 1.791640986550010028, 1.784033659547276329, 1.776464495522344977, 1.768932414909077933, + 1.761436365316706665, 1.753975320315455111, 1.746548278279492994, 1.739154261283669012, + 1.731792314050707216, 1.724461502945775715, 1.717160915015540690, 1.709889657069006086, + 1.702646854797613907, 1.695431651932238548, 1.688243209434858727, 1.681080704722823338, + 1.673943330923760353, 1.666830296159286684, 1.659740822855789499, 1.652674147080648526, + 1.645629517902360339, 1.638606196773111146, 1.631603456932422036, 1.624620582830568427, + 1.617656869570534228, 1.610711622367333673, 1.603784156023583041, 1.596873794420261339, + 1.589979870021648534, 1.583101723393471438, 1.576238702733332886, 1.569390163412534456, + 1.562555467528439657, 1.555733983466554893, 1.548925085471535512, 1.542128153226347553, + 1.535342571438843118, 1.528567729435024614, 1.521803020758293101, 1.515047842773992404, + 1.508301596278571965, 1.501563685112706548, 1.494833515777718391, 1.488110497054654369, + 1.481394039625375747, 1.474683555695025516, 1.467978458615230908, 1.461278162507407830, + 1.454582081885523293, 1.447889631277669675, 1.441200224845798017, 1.434513276002946425, + 1.427828197027290358, 1.421144398672323117, 1.414461289772464658, 1.407778276843371534, + 1.401094763676202559, 1.394410150925071257, 1.387723835686884621, 1.381035211072741964, + 1.374343665770030531, 1.367648583594317957, 1.360949343030101844, 1.354245316759430606, + 1.347535871177359290, 1.340820365893152122, 1.334098153216083604, 1.327368577624624679, + 1.320630975217730096, 1.313884673146868964, 1.307128989027353860, 1.300363230327433728, + 1.293586693733517645, 1.286798664489786415, 1.279998415710333237, 1.273185207661843732, + 1.266358287014688333, 1.259516886060144225, 1.252660221891297887, 1.245787495544997903, + 1.238897891102027415, 1.231990574742445110, 1.225064693752808020, 1.218119375481726552, + 1.211153726239911244, 1.204166830140560140, 1.197157747875585931, 1.190125515422801650, + 1.183069142678760732, 1.175987612011489825, 1.168879876726833800, 1.161744859441574240, + 1.154581450355851802, 1.147388505416733873, 1.140164844363995789, 1.132909248648336975, + 1.125620459211294389, 1.118297174115062909, 1.110938046009249502, 1.103541679420268151, + 1.096106627847603487, 1.088631390649514197, 1.081114409698889389, 1.073554065787871714, + 1.065948674757506653, 1.058296483326006454, 1.050595664586207123, 1.042844313139370538, + 1.035040439828605274, 1.027181966030751292, 1.019266717460529215, 1.011292417434978441, + 1.003256679539591412, 0.995156999629943084, 0.986990747093846266, 0.978755155288937750, + 0.970447311058864615, 0.962064143217605250, 0.953602409875572654, 0.945058684462571130, + 0.936429340280896860, 0.927710533396234771, 0.918898183643734989, 0.909987953490768997, + 0.900975224455174528, 0.891855070726792376, 0.882622229578910122, 0.873271068082494550, + 0.863795545546826915, 0.854189171001560554, 0.844444954902423661, 0.834555354079518752, + 0.824512208745288633, 0.814306670128064347, 0.803929116982664893, 0.793369058833152785, + 0.782615023299588763, 0.771654424216739354, 0.760473406422083165, 0.749056662009581653, + 0.737387211425838629, 0.725446140901303549, 0.713212285182022732, 0.700661841097584448, + 0.687767892786257717, 0.674499822827436479, 0.660822574234205984, 0.646695714884388928, + 0.632072236375024632, 0.616896989996235545, 0.601104617743940417, 0.584616766093722262, + 0.567338257040473026, 0.549151702313026790, 0.529909720646495108, 0.509423329585933393, + 0.487443966121754335, 0.463634336771763245, 0.437518402186662658, 0.408389134588000746, + 0.375121332850465727, 0.335737519180459465, 0.286174591747260509, 0.215241895913273806, + 0.000000000000000000]; +pub static ZIG_NORM_F: [f64; 257] = + [0.000477467764586655, 0.001260285930498598, 0.002609072746106363, 0.004037972593371872, + 0.005522403299264754, 0.007050875471392110, 0.008616582769422917, 0.010214971439731100, + 0.011842757857943104, 0.013497450601780807, 0.015177088307982072, 0.016880083152595839, + 0.018605121275783350, 0.020351096230109354, 0.022117062707379922, 0.023902203305873237, + 0.025705804008632656, 0.027527235669693315, 0.029365939758230111, 0.031221417192023690, + 0.033093219458688698, 0.034980941461833073, 0.036884215688691151, 0.038802707404656918, + 0.040736110656078753, 0.042684144916619378, 0.044646552251446536, 0.046623094902089664, + 0.048613553216035145, 0.050617723861121788, 0.052635418276973649, 0.054666461325077916, + 0.056710690106399467, 0.058767952921137984, 0.060838108349751806, 0.062921024437977854, + 0.065016577971470438, 0.067124653828023989, 0.069245144397250269, 0.071377949059141965, + 0.073522973714240991, 0.075680130359194964, 0.077849336702372207, 0.080030515814947509, + 0.082223595813495684, 0.084428509570654661, 0.086645194450867782, 0.088873592068594229, + 0.091113648066700734, 0.093365311913026619, 0.095628536713353335, 0.097903279039215627, + 0.100189498769172020, 0.102487158942306270, 0.104796225622867056, 0.107116667775072880, + 0.109448457147210021, 0.111791568164245583, 0.114145977828255210, 0.116511665626037014, + 0.118888613443345698, 0.121276805485235437, 0.123676228202051403, 0.126086870220650349, + 0.128508722280473636, 0.130941777174128166, 0.133386029692162844, 0.135841476571757352, + 0.138308116449064322, 0.140785949814968309, 0.143274978974047118, 0.145775208006537926, + 0.148286642733128721, 0.150809290682410169, 0.153343161060837674, 0.155888264725064563, + 0.158444614156520225, 0.161012223438117663, 0.163591108232982951, 0.166181285765110071, + 0.168782774801850333, 0.171395595638155623, 0.174019770082499359, 0.176655321444406654, + 0.179302274523530397, 0.181960655600216487, 0.184630492427504539, 0.187311814224516926, + 0.190004651671193070, 0.192709036904328807, 0.195425003514885592, 0.198152586546538112, + 0.200891822495431333, 0.203642749311121501, 0.206405406398679298, 0.209179834621935651, + 0.211966076307852941, 0.214764175252008499, 0.217574176725178370, 0.220396127481011589, + 0.223230075764789593, 0.226076071323264877, 0.228934165415577484, 0.231804410825248525, + 0.234686861873252689, 0.237581574432173676, 0.240488605941449107, 0.243408015423711988, + 0.246339863502238771, 0.249284212419516704, 0.252241126056943765, 0.255210669955677150, + 0.258192911338648023, 0.261187919133763713, 0.264195763998317568, 0.267216518344631837, + 0.270250256366959984, 0.273297054069675804, 0.276356989296781264, 0.279430141762765316, + 0.282516593084849388, 0.285616426816658109, 0.288729728483353931, 0.291856585618280984, + 0.294997087801162572, 0.298151326697901342, 0.301319396102034120, 0.304501391977896274, + 0.307697412505553769, 0.310907558127563710, 0.314131931597630143, 0.317370638031222396, + 0.320623784958230129, 0.323891482377732021, 0.327173842814958593, 0.330470981380537099, + 0.333783015832108509, 0.337110066638412809, 0.340452257045945450, 0.343809713148291340, + 0.347182563958251478, 0.350570941482881204, 0.353974980801569250, 0.357394820147290515, + 0.360830600991175754, 0.364282468130549597, 0.367750569780596226, 0.371235057669821344, + 0.374736087139491414, 0.378253817247238111, 0.381788410875031348, 0.385340034841733958, + 0.388908860020464597, 0.392495061461010764, 0.396098818517547080, 0.399720314981931668, + 0.403359739222868885, 0.407017284331247953, 0.410693148271983222, 0.414387534042706784, + 0.418100649839684591, 0.421832709231353298, 0.425583931339900579, 0.429354541031341519, + 0.433144769114574058, 0.436954852549929273, 0.440785034667769915, 0.444635565397727750, + 0.448506701509214067, 0.452398706863882505, 0.456311852680773566, 0.460246417814923481, + 0.464202689050278838, 0.468180961407822172, 0.472181538469883255, 0.476204732721683788, + 0.480250865911249714, 0.484320269428911598, 0.488413284707712059, 0.492530263646148658, + 0.496671569054796314, 0.500837575128482149, 0.505028667945828791, 0.509245245998136142, + 0.513487720749743026, 0.517756517232200619, 0.522052074674794864, 0.526374847174186700, + 0.530725304406193921, 0.535103932383019565, 0.539511234259544614, 0.543947731192649941, + 0.548413963257921133, 0.552910490428519918, 0.557437893621486324, 0.561996775817277916, + 0.566587763258951771, 0.571211506738074970, 0.575868682975210544, 0.580559996103683473, + 0.585286179266300333, 0.590047996335791969, 0.594846243770991268, 0.599681752622167719, + 0.604555390700549533, 0.609468064928895381, 0.614420723892076803, 0.619414360609039205, + 0.624450015550274240, 0.629528779928128279, 0.634651799290960050, 0.639820277456438991, + 0.645035480824251883, 0.650298743114294586, 0.655611470583224665, 0.660975147780241357, + 0.666391343912380640, 0.671861719900766374, 0.677388036222513090, 0.682972161648791376, + 0.688616083008527058, 0.694321916130032579, 0.700091918140490099, 0.705928501336797409, + 0.711834248882358467, 0.717811932634901395, 0.723864533472881599, 0.729995264565802437, + 0.736207598131266683, 0.742505296344636245, 0.748892447223726720, 0.755373506511754500, + 0.761953346841546475, 0.768637315803334831, 0.775431304986138326, 0.782341832659861902, + 0.789376143571198563, 0.796542330428254619, 0.803849483176389490, 0.811307874318219935, + 0.818929191609414797, 0.826726833952094231, 0.834716292992930375, 0.842915653118441077, + 0.851346258465123684, 0.860033621203008636, 0.869008688043793165, 0.878309655816146839, + 0.887984660763399880, 0.898095921906304051, 0.908726440060562912, 0.919991505048360247, + 0.932060075968990209, 0.945198953453078028, 0.959879091812415930, 0.977101701282731328, + 1.000000000000000000]; +pub const ZIG_EXP_R: f64 = 7.697117470131050077; +pub static ZIG_EXP_X: [f64; 257] = + [8.697117470131052741, 7.697117470131050077, 6.941033629377212577, 6.478378493832569696, + 6.144164665772472667, 5.882144315795399869, 5.666410167454033697, 5.482890627526062488, + 5.323090505754398016, 5.181487281301500047, 5.054288489981304089, 4.938777085901250530, + 4.832939741025112035, 4.735242996601741083, 4.644491885420085175, 4.559737061707351380, + 4.480211746528421912, 4.405287693473573185, 4.334443680317273007, 4.267242480277365857, + 4.203313713735184365, 4.142340865664051464, 4.084051310408297830, 4.028208544647936762, + 3.974606066673788796, 3.923062500135489739, 3.873417670399509127, 3.825529418522336744, + 3.779270992411667862, 3.734528894039797375, 3.691201090237418825, 3.649195515760853770, + 3.608428813128909507, 3.568825265648337020, 3.530315889129343354, 3.492837654774059608, + 3.456332821132760191, 3.420748357251119920, 3.386035442460300970, 3.352149030900109405, + 3.319047470970748037, 3.286692171599068679, 3.255047308570449882, 3.224079565286264160, + 3.193757903212240290, 3.164053358025972873, 3.134938858084440394, 3.106389062339824481, + 3.078380215254090224, 3.050890016615455114, 3.023897504455676621, 2.997382949516130601, + 2.971327759921089662, 2.945714394895045718, 2.920526286512740821, 2.895747768600141825, + 2.871364012015536371, 2.847360965635188812, 2.823725302450035279, 2.800444370250737780, + 2.777506146439756574, 2.754899196562344610, 2.732612636194700073, 2.710636095867928752, + 2.688959688741803689, 2.667573980773266573, 2.646469963151809157, 2.625639026797788489, + 2.605072938740835564, 2.584763820214140750, 2.564704126316905253, 2.544886627111869970, + 2.525304390037828028, 2.505950763528594027, 2.486819361740209455, 2.467904050297364815, + 2.449198932978249754, 2.430698339264419694, 2.412396812688870629, 2.394289099921457886, + 2.376370140536140596, 2.358635057409337321, 2.341079147703034380, 2.323697874390196372, + 2.306486858283579799, 2.289441870532269441, 2.272558825553154804, 2.255833774367219213, + 2.239262898312909034, 2.222842503111036816, 2.206569013257663858, 2.190438966723220027, + 2.174449009937774679, 2.158595893043885994, 2.142876465399842001, 2.127287671317368289, + 2.111826546019042183, 2.096490211801715020, 2.081275874393225145, 2.066180819490575526, + 2.051202409468584786, 2.036338080248769611, 2.021585338318926173, 2.006941757894518563, + 1.992404978213576650, 1.977972700957360441, 1.963642687789548313, 1.949412758007184943, + 1.935280786297051359, 1.921244700591528076, 1.907302480018387536, 1.893452152939308242, + 1.879691795072211180, 1.866019527692827973, 1.852433515911175554, 1.838931967018879954, + 1.825513128903519799, 1.812175288526390649, 1.798916770460290859, 1.785735935484126014, + 1.772631179231305643, 1.759600930889074766, 1.746643651946074405, 1.733757834985571566, + 1.720942002521935299, 1.708194705878057773, 1.695514524101537912, 1.682900062917553896, + 1.670349953716452118, 1.657862852574172763, 1.645437439303723659, 1.633072416535991334, + 1.620766508828257901, 1.608518461798858379, 1.596327041286483395, 1.584191032532688892, + 1.572109239386229707, 1.560080483527888084, 1.548103603714513499, 1.536177455041032092, + 1.524300908219226258, 1.512472848872117082, 1.500692176842816750, 1.488957805516746058, + 1.477268661156133867, 1.465623682245745352, 1.454021818848793446, 1.442462031972012504, + 1.430943292938879674, 1.419464582769983219, 1.408024891569535697, 1.396623217917042137, + 1.385258568263121992, 1.373929956328490576, 1.362636402505086775, 1.351376933258335189, + 1.340150580529504643, 1.328956381137116560, 1.317793376176324749, 1.306660610415174117, + 1.295557131686601027, 1.284481990275012642, 1.273434238296241139, 1.262412929069615330, + 1.251417116480852521, 1.240445854334406572, 1.229498195693849105, 1.218573192208790124, + 1.207669893426761121, 1.196787346088403092, 1.185924593404202199, 1.175080674310911677, + 1.164254622705678921, 1.153445466655774743, 1.142652227581672841, 1.131873919411078511, + 1.121109547701330200, 1.110358108727411031, 1.099618588532597308, 1.088889961938546813, + 1.078171191511372307, 1.067461226479967662, 1.056759001602551429, 1.046063435977044209, + 1.035373431790528542, 1.024687873002617211, 1.014005623957096480, 1.003325527915696735, + 0.992646405507275897, 0.981967053085062602, 0.971286240983903260, 0.960602711668666509, + 0.949915177764075969, 0.939222319955262286, 0.928522784747210395, 0.917815182070044311, + 0.907098082715690257, 0.896370015589889935, 0.885629464761751528, 0.874874866291025066, + 0.864104604811004484, 0.853317009842373353, 0.842510351810368485, 0.831682837734273206, + 0.820832606554411814, 0.809957724057418282, 0.799056177355487174, 0.788125868869492430, + 0.777164609759129710, 0.766170112735434672, 0.755139984181982249, 0.744071715500508102, + 0.732962673584365398, 0.721810090308756203, 0.710611050909655040, 0.699362481103231959, + 0.688061132773747808, 0.676703568029522584, 0.665286141392677943, 0.653804979847664947, + 0.642255960424536365, 0.630634684933490286, 0.618936451394876075, 0.607156221620300030, + 0.595288584291502887, 0.583327712748769489, 0.571267316532588332, 0.559100585511540626, + 0.546820125163310577, 0.534417881237165604, 0.521885051592135052, 0.509211982443654398, + 0.496388045518671162, 0.483401491653461857, 0.470239275082169006, 0.456886840931420235, + 0.443327866073552401, 0.429543940225410703, 0.415514169600356364, 0.401214678896277765, + 0.386617977941119573, 0.371692145329917234, 0.356399760258393816, 0.340696481064849122, + 0.324529117016909452, 0.307832954674932158, 0.290527955491230394, 0.272513185478464703, + 0.253658363385912022, 0.233790483059674731, 0.212671510630966620, 0.189958689622431842, + 0.165127622564187282, 0.137304980940012589, 0.104838507565818778, 0.063852163815001570, + 0.000000000000000000]; +pub static ZIG_EXP_F: [f64; 257] = + [0.000167066692307963, 0.000454134353841497, 0.000967269282327174, 0.001536299780301573, + 0.002145967743718907, 0.002788798793574076, 0.003460264777836904, 0.004157295120833797, + 0.004877655983542396, 0.005619642207205489, 0.006381905937319183, 0.007163353183634991, + 0.007963077438017043, 0.008780314985808977, 0.009614413642502212, 0.010464810181029981, + 0.011331013597834600, 0.012212592426255378, 0.013109164931254991, 0.014020391403181943, + 0.014945968011691148, 0.015885621839973156, 0.016839106826039941, 0.017806200410911355, + 0.018786700744696024, 0.019780424338009740, 0.020787204072578114, 0.021806887504283581, + 0.022839335406385240, 0.023884420511558174, 0.024942026419731787, 0.026012046645134221, + 0.027094383780955803, 0.028188948763978646, 0.029295660224637411, 0.030414443910466622, + 0.031545232172893622, 0.032687963508959555, 0.033842582150874358, 0.035009037697397431, + 0.036187284781931443, 0.037377282772959382, 0.038578995503074871, 0.039792391023374139, + 0.041017441380414840, 0.042254122413316254, 0.043502413568888197, 0.044762297732943289, + 0.046033761076175184, 0.047316792913181561, 0.048611385573379504, 0.049917534282706379, + 0.051235237055126281, 0.052564494593071685, 0.053905310196046080, 0.055257689676697030, + 0.056621641283742870, 0.057997175631200659, 0.059384305633420280, 0.060783046445479660, + 0.062193415408541036, 0.063615431999807376, 0.065049117786753805, 0.066494496385339816, + 0.067951593421936643, 0.069420436498728783, 0.070901055162371843, 0.072393480875708752, + 0.073897746992364746, 0.075413888734058410, 0.076941943170480517, 0.078481949201606435, + 0.080033947542319905, 0.081597980709237419, 0.083174093009632397, 0.084762330532368146, + 0.086362741140756927, 0.087975374467270231, 0.089600281910032886, 0.091237516631040197, + 0.092887133556043569, 0.094549189376055873, 0.096223742550432825, 0.097910853311492213, + 0.099610583670637132, 0.101322997425953631, 0.103048160171257702, 0.104786139306570145, + 0.106537004050001632, 0.108300825451033755, 0.110077676405185357, 0.111867631670056283, + 0.113670767882744286, 0.115487163578633506, 0.117316899211555525, 0.119160057175327641, + 0.121016721826674792, 0.122886979509545108, 0.124770918580830933, 0.126668629437510671, + 0.128580204545228199, 0.130505738468330773, 0.132445327901387494, 0.134399071702213602, + 0.136367070926428829, 0.138349428863580176, 0.140346251074862399, 0.142357645432472146, + 0.144383722160634720, 0.146424593878344889, 0.148480375643866735, 0.150551185001039839, + 0.152637142027442801, 0.154738369384468027, 0.156854992369365148, 0.158987138969314129, + 0.161134939917591952, 0.163298528751901734, 0.165478041874935922, 0.167673618617250081, + 0.169885401302527550, 0.172113535315319977, 0.174358169171353411, 0.176619454590494829, + 0.178897546572478278, 0.181192603475496261, 0.183504787097767436, 0.185834262762197083, + 0.188181199404254262, 0.190545769663195363, 0.192928149976771296, 0.195328520679563189, + 0.197747066105098818, 0.200183974691911210, 0.202639439093708962, 0.205113656293837654, + 0.207606827724221982, 0.210119159388988230, 0.212650861992978224, 0.215202151075378628, + 0.217773247148700472, 0.220364375843359439, 0.222975768058120111, 0.225607660116683956, + 0.228260293930716618, 0.230933917169627356, 0.233628783437433291, 0.236345152457059560, + 0.239083290262449094, 0.241843469398877131, 0.244625969131892024, 0.247431075665327543, + 0.250259082368862240, 0.253110290015629402, 0.255985007030415324, 0.258883549749016173, + 0.261806242689362922, 0.264753418835062149, 0.267725419932044739, 0.270722596799059967, + 0.273745309652802915, 0.276793928448517301, 0.279868833236972869, 0.282970414538780746, + 0.286099073737076826, 0.289255223489677693, 0.292439288161892630, 0.295651704281261252, + 0.298892921015581847, 0.302163400675693528, 0.305463619244590256, 0.308794066934560185, + 0.312155248774179606, 0.315547685227128949, 0.318971912844957239, 0.322428484956089223, + 0.325917972393556354, 0.329440964264136438, 0.332998068761809096, 0.336589914028677717, + 0.340217149066780189, 0.343880444704502575, 0.347580494621637148, 0.351318016437483449, + 0.355093752866787626, 0.358908472948750001, 0.362762973354817997, 0.366658079781514379, + 0.370594648435146223, 0.374573567615902381, 0.378595759409581067, 0.382662181496010056, + 0.386773829084137932, 0.390931736984797384, 0.395136981833290435, 0.399390684475231350, + 0.403694012530530555, 0.408048183152032673, 0.412454465997161457, 0.416914186433003209, + 0.421428728997616908, 0.425999541143034677, 0.430628137288459167, 0.435316103215636907, + 0.440065100842354173, 0.444876873414548846, 0.449753251162755330, 0.454696157474615836, + 0.459707615642138023, 0.464789756250426511, 0.469944825283960310, 0.475175193037377708, + 0.480483363930454543, 0.485871987341885248, 0.491343869594032867, 0.496901987241549881, + 0.502549501841348056, 0.508289776410643213, 0.514126393814748894, 0.520063177368233931, + 0.526104213983620062, 0.532253880263043655, 0.538516872002862246, 0.544898237672440056, + 0.551403416540641733, 0.558038282262587892, 0.564809192912400615, 0.571723048664826150, + 0.578787358602845359, 0.586010318477268366, 0.593400901691733762, 0.600968966365232560, + 0.608725382079622346, 0.616682180915207878, 0.624852738703666200, 0.633251994214366398, + 0.641896716427266423, 0.650805833414571433, 0.660000841079000145, 0.669506316731925177, + 0.679350572264765806, 0.689566496117078431, 0.700192655082788606, 0.711274760805076456, + 0.722867659593572465, 0.735038092431424039, 0.747868621985195658, 0.761463388849896838, + 0.775956852040116218, 0.791527636972496285, 0.808421651523009044, 0.826993296643051101, + 0.847785500623990496, 0.871704332381204705, 0.900469929925747703, 0.938143680862176477, + 1.000000000000000000]; diff --git a/rand/tests/uniformity.rs b/rand/rand_distr/tests/uniformity.rs index b8f74a6..d0d9d97 100644 --- a/rand/tests/uniformity.rs +++ b/rand/rand_distr/tests/uniformity.rs @@ -6,21 +6,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![cfg(feature = "std")] - -#[macro_use] -extern crate average; -extern crate rand; - -use std as core; -use rand::FromEntropy; -use rand::distributions::Distribution; use average::Histogram; +use rand::prelude::*; const N_BINS: usize = 100; const N_SAMPLES: u32 = 1_000_000; const TOL: f64 = 1e-3; -define_histogram!(hist, 100); +average::define_histogram!(hist, 100); use hist::Histogram as Histogram100; #[test] @@ -28,10 +20,10 @@ fn unit_sphere() { const N_DIM: usize = 3; let h = Histogram100::with_const_width(-1., 1.); let mut histograms = [h.clone(), h.clone(), h]; - let dist = rand::distributions::UnitSphereSurface::new(); - let mut rng = rand::rngs::SmallRng::from_entropy(); + let dist = rand_distr::UnitSphere; + let mut rng = rand_pcg::Pcg32::from_entropy(); for _ in 0..N_SAMPLES { - let v = dist.sample(&mut rng); + let v: [f64; 3] = dist.sample(&mut rng); for i in 0..N_DIM { histograms[i].add(v[i]).map_err( |e| { println!("v: {}", v[i]); e } @@ -50,12 +42,12 @@ fn unit_sphere() { #[test] fn unit_circle() { - use ::std::f64::consts::PI; + use std::f64::consts::PI; let mut h = Histogram100::with_const_width(-PI, PI); - let dist = rand::distributions::UnitCircle::new(); - let mut rng = rand::rngs::SmallRng::from_entropy(); + let dist = rand_distr::UnitCircle; + let mut rng = rand_pcg::Pcg32::from_entropy(); for _ in 0..N_SAMPLES { - let v = dist.sample(&mut rng); + let v: [f64; 2] = dist.sample(&mut rng); h.add(v[0].atan2(v[1])).unwrap(); } let sum: u64 = h.bins().iter().sum(); diff --git a/rand/rand_hc/CHANGELOG.md b/rand/rand_hc/CHANGELOG.md index d0c4a2f..a629d7d 100644 --- a/rand/rand_hc/CHANGELOG.md +++ b/rand/rand_hc/CHANGELOG.md @@ -4,5 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2019-06-12 +- Bump minor crate version since rand_core bump is a breaking change +- Switch to Edition 2018 + +## [0.1.1] - 2019-06-06 - yanked +- Bump `rand_core` version +- Adjust usage of `#[inline]` + ## [0.1.0] - 2018-10-17 - Pulled out of the Rand crate diff --git a/rand/rand_hc/Cargo.toml b/rand/rand_hc/Cargo.toml index ed5dd5b..40cea06 100644 --- a/rand/rand_hc/Cargo.toml +++ b/rand/rand_hc/Cargo.toml @@ -1,21 +1,22 @@ [package] name = "rand_hc" -version = "0.1.0" +version = "0.2.0" authors = ["The Rand Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" -documentation = "https://rust-random.github.io/rand/rand_hc" +documentation = "https://rust-random.github.io/rand/rand_hc/" homepage = "https://crates.io/crates/rand_hc" description = """ HC128 random number generator """ keywords = ["random", "rng", "hc128"] categories = ["algorithms", "no-std"] +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } [dependencies] -rand_core = { path = "../rand_core", version = ">=0.2, <0.4", default-features=false } +rand_core = { path = "../rand_core", version = "0.5" } diff --git a/rand/rand_hc/README.md b/rand/rand_hc/README.md index 178548a..36449c0 100644 --- a/rand/rand_hc/README.md +++ b/rand/rand_hc/README.md @@ -6,7 +6,7 @@ [[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_hc) [![API](https://docs.rs/rand_hc/badge.svg)](https://docs.rs/rand_hc) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) A cryptographically secure random number generator that uses the HC-128 algorithm. @@ -19,7 +19,7 @@ Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_hc) - [API documentation (docs.rs)](https://docs.rs/rand_hc) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_hc/CHANGELOG.md) [rand]: https://crates.io/crates/rand [^1]: Hongjun Wu (2008). ["The Stream Cipher HC-128"]( diff --git a/rand/rand_hc/src/hc128.rs b/rand/rand_hc/src/hc128.rs index d1dadcc..a320f48 100644 --- a/rand/rand_hc/src/hc128.rs +++ b/rand/rand_hc/src/hc128.rs @@ -63,27 +63,26 @@ const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv /// /// [^5]: Internet Engineering Task Force (February 2015), /// ["Prohibiting RC4 Cipher Suites"](https://tools.ietf.org/html/rfc7465). -/// -/// [`BlockRng`]: ../rand_core/block/struct.BlockRng.html -/// [`RngCore`]: ../rand_core/trait.RngCore.html #[derive(Clone, Debug)] pub struct Hc128Rng(BlockRng<Hc128Core>); impl RngCore for Hc128Rng { - #[inline(always)] + #[inline] fn next_u32(&mut self) -> u32 { self.0.next_u32() } - #[inline(always)] + #[inline] fn next_u64(&mut self) -> u64 { self.0.next_u64() } + #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { self.0.fill_bytes(dest) } + #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.0.try_fill_bytes(dest) } @@ -92,10 +91,12 @@ impl RngCore for Hc128Rng { impl SeedableRng for Hc128Rng { type Seed = <Hc128Core as SeedableRng>::Seed; + #[inline] fn from_seed(seed: Self::Seed) -> Self { Hc128Rng(BlockRng::<Hc128Core>::from_seed(seed)) } + #[inline] fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { BlockRng::<Hc128Core>::from_rng(rng).map(Hc128Rng) } @@ -271,6 +272,7 @@ impl Hc128Core { // Initialize an HC-128 random number generator. The seed has to be // 256 bits in length (`[u32; 8]`), matching the 128 bit `key` followed by // 128 bit `iv` when HC-128 where to be used as a stream cipher. + #[inline(always)] // single use: SeedableRng::from_seed fn init(seed: [u32; SEED_WORDS]) -> Self { #[inline] fn f1(x: u32) -> u32 { diff --git a/rand/rand_hc/src/lib.rs b/rand/rand_hc/src/lib.rs index 10466cf..c1ae665 100644 --- a/rand/rand_hc/src/lib.rs +++ b/rand/rand_hc/src/lib.rs @@ -18,8 +18,6 @@ #![no_std] -pub extern crate rand_core; - mod hc128; pub use hc128::{Hc128Rng, Hc128Core}; diff --git a/rand/rand_isaac/CHANGELOG.md b/rand/rand_isaac/CHANGELOG.md index fb1ab3f..0a5591f 100644 --- a/rand/rand_isaac/CHANGELOG.md +++ b/rand/rand_isaac/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2019-06-12 +- Bump minor crate version since rand_core bump is a breaking change +- Switch to Edition 2018 + +## [0.1.2] - 2019-06-06 - yanked +- Bump `rand_core` version +- Remove deprecated code +- Adjust usage of `#[inline]` + ## [0.1.1] - 2018-11-26 - Fix `rand_core` version requirement - Fix doc links diff --git a/rand/rand_isaac/Cargo.toml b/rand/rand_isaac/Cargo.toml index b35d0ab..c11c305 100644 --- a/rand/rand_isaac/Cargo.toml +++ b/rand/rand_isaac/Cargo.toml @@ -1,29 +1,29 @@ [package] name = "rand_isaac" -version = "0.1.1" +version = "0.2.0" authors = ["The Rand Project Developers", "The Rust Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" -documentation = "https://rust-random.github.io/rand/rand_isaac" +documentation = "https://rust-random.github.io/rand/rand_isaac/" homepage = "https://crates.io/crates/rand_isaac" description = """ ISAAC random number generator """ keywords = ["random", "rng", "isaac"] categories = ["algorithms", "no-std"] +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } [features] -serde1 = ["serde", "serde_derive", "rand_core/serde1"] +serde1 = ["serde", "rand_core/serde1"] [dependencies] -rand_core = { path = "../rand_core", version = "0.3", default-features=false } -serde = { version = "1", optional = true } -serde_derive = { version = "^1.0.38", optional = true } +rand_core = { path = "../rand_core", version = "0.5" } +serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] # This is for testing serde, unfortunately we can't specify feature-gated dev diff --git a/rand/rand_isaac/README.md b/rand/rand_isaac/README.md index 02d1230..c16c63f 100644 --- a/rand/rand_isaac/README.md +++ b/rand/rand_isaac/README.md @@ -6,7 +6,7 @@ [![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_isaac) [![API](https://docs.rs/rand_isaac/badge.svg)](https://docs.rs/rand_isaac) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) Implements the ISAAC and ISAAC-64 random number generators. @@ -22,7 +22,7 @@ Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_isaac) - [API documentation (docs.rs)](https://docs.rs/rand_isaac) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_isaac/CHANGELOG.md) [rand]: https://crates.io/crates/rand [^1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number generator*](http://burtleburtle.net/bob/rand/isaacafa.html) diff --git a/rand/rand_isaac/src/isaac.rs b/rand/rand_isaac/src/isaac.rs index 2bfdd94..2caf61a 100644 --- a/rand/rand_isaac/src/isaac.rs +++ b/rand/rand_isaac/src/isaac.rs @@ -11,9 +11,10 @@ use core::{fmt, slice}; use core::num::Wrapping as w; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::{RngCore, SeedableRng, Error, le}; use rand_core::block::{BlockRngCore, BlockRng}; -use isaac_array::IsaacArray; +use crate::isaac_array::IsaacArray; #[allow(non_camel_case_types)] type w32 = w<u32>; @@ -34,8 +35,8 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// In spite of being designed with cryptographic security in mind, ISAAC hasn't /// been stringently cryptanalyzed and thus cryptographers do not not /// consensually trust it to be secure. When looking for a secure RNG, prefer -/// [`Hc128Rng`] instead, which, like ISAAC, is an array-based RNG and one of -/// the stream-ciphers selected the by eSTREAM contest. +/// `Hc128Rng` from the [`rand_hc`] crate instead, which, like ISAAC, is an +/// array-based RNG and one of the stream-ciphers selected the by eSTREAM /// /// In 2006 an improvement to ISAAC was suggested by Jean-Philippe Aumasson, /// named ISAAC+[^3]. But because the specification is not complete, because @@ -86,28 +87,28 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [^3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*]( /// https://eprint.iacr.org/2006/438) /// -/// [`Hc128Rng`]: ../../rand_hc/struct.Hc128Rng.html -/// [`BlockRng`]: ../../rand_core/block/struct.BlockRng.html -/// [`RngCore`]: ../../rand_core/trait.RngCore.html +/// [`rand_hc`]: https://docs.rs/rand_hc #[derive(Clone, Debug)] #[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct IsaacRng(BlockRng<IsaacCore>); impl RngCore for IsaacRng { - #[inline(always)] + #[inline] fn next_u32(&mut self) -> u32 { self.0.next_u32() } - #[inline(always)] + #[inline] fn next_u64(&mut self) -> u64 { self.0.next_u64() } + #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { self.0.fill_bytes(dest) } + #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.0.try_fill_bytes(dest) } @@ -116,33 +117,26 @@ impl RngCore for IsaacRng { impl SeedableRng for IsaacRng { type Seed = <IsaacCore as SeedableRng>::Seed; + #[inline] fn from_seed(seed: Self::Seed) -> Self { IsaacRng(BlockRng::<IsaacCore>::from_seed(seed)) } - + /// Create an ISAAC random number generator using an `u64` as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. + #[inline] fn seed_from_u64(seed: u64) -> Self { IsaacRng(BlockRng::<IsaacCore>::seed_from_u64(seed)) } + #[inline] fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> { BlockRng::<IsaacCore>::from_rng(rng).map(|rng| IsaacRng(rng)) } } -impl IsaacRng { - /// Create an ISAAC random number generator using an `u64` as seed. - /// If `seed == 0` this will produce the same stream of random numbers as - /// the reference implementation when used unseeded. - #[deprecated(since="0.6.0", note="use SeedableRng::seed_from_u64 instead")] - pub fn new_from_u64(seed: u64) -> Self { - Self::seed_from_u64(seed) - } -} - -/// The core of `IsaacRng`, used with `BlockRng`. +/// The core of [`IsaacRng`], used with [`BlockRng`]. #[derive(Clone)] #[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct IsaacCore { @@ -165,10 +159,10 @@ impl BlockRngCore for IsaacCore { type Results = IsaacArray<Self::Item>; /// Refills the output buffer, `results`. See also the pseudocode desciption - /// of the algorithm in the [`IsaacRng`] documentation. + /// of the algorithm in the `IsaacRng` documentation. /// /// Optimisations used (similar to the reference implementation): - /// + /// /// - The loop is unrolled 4 times, once for every constant of mix(). /// - The contents of the main loop are moved to a function `rngstep`, to /// reduce code duplication. @@ -183,8 +177,6 @@ impl BlockRngCore for IsaacCore { /// from `results` in reverse. We read them in the normal direction, to /// make `fill_bytes` a memcopy. To maintain compatibility we fill in /// reverse. - /// - /// [`IsaacRng`]: struct.IsaacRng.html fn generate(&mut self, results: &mut IsaacArray<Self::Item>) { self.c += w(1); // abbreviations @@ -324,7 +316,7 @@ impl SeedableRng for IsaacCore { } Self::init(seed_extended, 2) } - + /// Create an ISAAC random number generator using an `u64` as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. diff --git a/rand/rand_isaac/src/isaac64.rs b/rand/rand_isaac/src/isaac64.rs index 2712762..7d4b88c 100644 --- a/rand/rand_isaac/src/isaac64.rs +++ b/rand/rand_isaac/src/isaac64.rs @@ -11,9 +11,10 @@ use core::{fmt, slice}; use core::num::Wrapping as w; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::{RngCore, SeedableRng, Error, le}; use rand_core::block::{BlockRngCore, BlockRng64}; -use isaac_array::IsaacArray; +use crate::isaac_array::IsaacArray; #[allow(non_camel_case_types)] type w64 = w<u64>; @@ -40,8 +41,8 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// In spite of being designed with cryptographic security in mind, ISAAC hasn't /// been stringently cryptanalyzed and thus cryptographers do not not /// consensually trust it to be secure. When looking for a secure RNG, prefer -/// [`Hc128Rng`] instead, which, like ISAAC, is an array-based RNG and one of -/// the stream-ciphers selected the by eSTREAM contest. +/// `Hc128Rng` from the [`rand_hc`] crate instead, which, like ISAAC, is an +/// array-based RNG and one of the stream-ciphers selected the by eSTREAM /// /// ## Overview of the ISAAC-64 algorithm: /// (in pseudo-code) @@ -75,29 +76,30 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [^1]: Bob Jenkins, [*ISAAC and RC4*]( /// http://burtleburtle.net/bob/rand/isaac.html) /// -/// [`IsaacRng`]: ../isaac/struct.IsaacRng.html -/// [`Hc128Rng`]: ../../rand_hc/struct.Hc128Rng.html -/// [`BlockRng64`]: ../../rand_core/block/struct.BlockRng64.html -/// [`RngCore`]: ../../rand_core/trait.RngCore.html +/// [`IsaacRng`]: crate::isaac::IsaacRng +/// [`rand_hc`]: https://docs.rs/rand_hc +/// [`BlockRng64`]: rand_core::block::BlockRng64 #[derive(Clone, Debug)] #[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Isaac64Rng(BlockRng64<Isaac64Core>); impl RngCore for Isaac64Rng { - #[inline(always)] + #[inline] fn next_u32(&mut self) -> u32 { self.0.next_u32() } - #[inline(always)] + #[inline] fn next_u64(&mut self) -> u64 { self.0.next_u64() } + #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { self.0.fill_bytes(dest) } + #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.0.try_fill_bytes(dest) } @@ -106,6 +108,7 @@ impl RngCore for Isaac64Rng { impl SeedableRng for Isaac64Rng { type Seed = <Isaac64Core as SeedableRng>::Seed; + #[inline] fn from_seed(seed: Self::Seed) -> Self { Isaac64Rng(BlockRng64::<Isaac64Core>::from_seed(seed)) } @@ -113,25 +116,17 @@ impl SeedableRng for Isaac64Rng { /// Create an ISAAC random number generator using an `u64` as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. + #[inline] fn seed_from_u64(seed: u64) -> Self { Isaac64Rng(BlockRng64::<Isaac64Core>::seed_from_u64(seed)) } + #[inline] fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> { BlockRng64::<Isaac64Core>::from_rng(rng).map(|rng| Isaac64Rng(rng)) } } -impl Isaac64Rng { - /// Create an ISAAC-64 random number generator using an `u64` as seed. - /// If `seed == 0` this will produce the same stream of random numbers as - /// the reference implementation when used unseeded. - #[deprecated(since="0.6.0", note="use SeedableRng::seed_from_u64 instead")] - pub fn new_from_u64(seed: u64) -> Self { - Self::seed_from_u64(seed) - } -} - /// The core of `Isaac64Rng`, used with `BlockRng`. #[derive(Clone)] #[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] @@ -155,10 +150,10 @@ impl BlockRngCore for Isaac64Core { type Results = IsaacArray<Self::Item>; /// Refills the output buffer, `results`. See also the pseudocode desciption - /// of the algorithm in the [`Isaac64Rng`] documentation. + /// of the algorithm in the `Isaac64Rng` documentation. /// /// Optimisations used (similar to the reference implementation): - /// + /// /// - The loop is unrolled 4 times, once for every constant of mix(). /// - The contents of the main loop are moved to a function `rngstep`, to /// reduce code duplication. @@ -173,8 +168,6 @@ impl BlockRngCore for Isaac64Core { /// from `results` in reverse. We read them in the normal direction, to /// make `fill_bytes` a memcopy. To maintain compatibility we fill in /// reverse. - /// - /// [`Isaac64Rng`]: struct.Isaac64Rng.html fn generate(&mut self, results: &mut IsaacArray<Self::Item>) { self.c += w(1); // abbreviations @@ -274,14 +267,6 @@ impl Isaac64Core { Self { mem, a: w(0), b: w(0), c: w(0) } } - - /// Create an ISAAC-64 random number generator using an `u64` as seed. - /// If `seed == 0` this will produce the same stream of random numbers as - /// the reference implementation when used unseeded. - #[deprecated(since="0.6.0", note="use SeedableRng::seed_from_u64 instead")] - pub fn new_from_u64(seed: u64) -> Self { - Self::seed_from_u64(seed) - } } impl SeedableRng for Isaac64Core { @@ -297,7 +282,7 @@ impl SeedableRng for Isaac64Core { } Self::init(seed_extended, 2) } - + fn seed_from_u64(seed: u64) -> Self { let mut key = [w(0); RAND_SIZE]; key[0] = w(seed); diff --git a/rand/rand_isaac/src/isaac_array.rs b/rand/rand_isaac/src/isaac_array.rs index 0fa6147..cbe4a59 100644 --- a/rand/rand_isaac/src/isaac_array.rs +++ b/rand/rand_isaac/src/isaac_array.rs @@ -13,7 +13,7 @@ // implement `AsRef`, `Default`, `Serialize`, `Deserialize`, or any other // traits for that matter. -#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; +#[cfg(feature="serde")] use serde::{Serialize, Deserialize}; const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; @@ -21,10 +21,10 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; #[derive(Copy, Clone)] #[allow(missing_debug_implementations)] -#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde", derive(Serialize, Deserialize))] pub struct IsaacArray<T> { - #[cfg_attr(feature="serde1",serde(with="isaac_array_serde"))] - #[cfg_attr(feature="serde1", serde(bound( + #[cfg_attr(feature="serde",serde(with="isaac_array_serde"))] + #[cfg_attr(feature="serde", serde(bound( serialize = "T: Serialize", deserialize = "T: Deserialize<'de> + Copy + Default")))] inner: [T; RAND_SIZE] @@ -66,7 +66,7 @@ impl<T> ::core::default::Default for IsaacArray<T> where T: Copy + Default { } -#[cfg(feature="serde1")] +#[cfg(feature="serde")] pub(super) mod isaac_array_serde { const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; diff --git a/rand/rand_isaac/src/lib.rs b/rand/rand_isaac/src/lib.rs index 285d631..84cdf21 100644 --- a/rand/rand_isaac/src/lib.rs +++ b/rand/rand_isaac/src/lib.rs @@ -16,16 +16,7 @@ #![deny(missing_debug_implementations)] #![doc(test(attr(allow(unused_variables), deny(warnings))))] -#![cfg_attr(not(all(feature="serde1", test)), no_std)] - -pub extern crate rand_core; - -#[cfg(feature="serde1")] extern crate serde; -#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive; - -// To test serialization we need bincode and the standard library -#[cfg(all(feature="serde1", test))] extern crate bincode; -#[cfg(all(feature="serde1", test))] extern crate std as core; +#![cfg_attr(not(all(feature="serde", test)), no_std)] pub mod isaac; pub mod isaac64; diff --git a/rand/rand_jitter/CHANGELOG.md b/rand/rand_jitter/CHANGELOG.md new file mode 100644 index 0000000..9f4bb7e --- /dev/null +++ b/rand/rand_jitter/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.1] - 2019-08-16 +### Changed +- `TimerError` changed to `repr(u32)` (#864) +- `TimerError` enum values all increased by `1<<30` to match new `rand_core::Error` range (#864) + +## [0.2.0] - 2019-06-06 +- Bump `rand_core` version +- Support new `Error` type in `rand_core` 0.5 +- Remove CryptoRng trait bound (#699, #814) +- Enable doc-testing of README + +## [0.1.4] - 2019-05-02 +- Change error conversion code to partially fix #738 + +## [0.1.3] - 2019-02-05 +- Use libc in `no_std` mode to fix #723 + +## [0.1.2] - 2019-01-31 +- Fix for older rustc compilers on Windows (#722) + +## [0.1.1] - 2019-01-29 +- Fix for older rustc compilers on Mac OSX / iOS (#720) +- Misc. doc fixes + +## [0.1.0] - 2019-01-24 +Initial release. diff --git a/rand/rand_jitter/COPYRIGHT b/rand/rand_jitter/COPYRIGHT new file mode 100644 index 0000000..468d907 --- /dev/null +++ b/rand/rand_jitter/COPYRIGHT @@ -0,0 +1,12 @@ +Copyrights in the Rand project are retained by their contributors. No +copyright assignment is required to contribute to the Rand project. + +For full authorship information, see the version control history. + +Except as otherwise noted (below and/or in individual files), Rand is +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. + +The Rand project includes code from the Rust project +published under these same licenses. diff --git a/rand/rand_jitter/Cargo.toml b/rand/rand_jitter/Cargo.toml new file mode 100644 index 0000000..5b7e3c3 --- /dev/null +++ b/rand/rand_jitter/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "rand_jitter" +version = "0.2.1" +authors = ["The Rand Project Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-random/rand" +documentation = "https://docs.rs/rand_jitter" +description = "Random number generator based on timing jitter" +keywords = ["random", "rng", "os"] +edition = "2018" + +[badges] +travis-ci = { repository = "rust-random/rand" } +appveyor = { repository = "rust-random/rand" } + +[dependencies] +rand_core = { path = "../rand_core", version = "0.5" } +log = { version = "0.4", optional = true } + +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +# We don't need the 'use_std' feature and depending on it causes +# issues due to: https://github.com/rust-lang/cargo/issues/1197 +libc = { version = "0.2", default_features = false } + +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3", features = ["profileapi"] } + +[features] +std = ["rand_core/std"] diff --git a/rand/rand_jitter/LICENSE-APACHE b/rand/rand_jitter/LICENSE-APACHE new file mode 100644 index 0000000..17d7468 --- /dev/null +++ b/rand/rand_jitter/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rand/rand_jitter/LICENSE-MIT b/rand/rand_jitter/LICENSE-MIT new file mode 100644 index 0000000..d93b5ba --- /dev/null +++ b/rand/rand_jitter/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright 2018 Developers of the Rand project +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/rand/rand_jitter/README.md b/rand/rand_jitter/README.md new file mode 100644 index 0000000..2091d6c --- /dev/null +++ b/rand/rand_jitter/README.md @@ -0,0 +1,119 @@ +# rand_jitter +[![Build Status](https://travis-ci.org/rust-random/rand.svg?branch=master)](https://travis-ci.org/rust-random/rand) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-random/rand?svg=true)](https://ci.appveyor.com/project/rust-random/rand) +[![Latest version](https://img.shields.io/crates/v/rand_jitter.svg)](https://crates.io/crates/rand_jitter) +[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) +[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_jitter) +[![API](https://docs.rs/rand_jitter/badge.svg)](https://docs.rs/rand_jitter) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) + +Non-physical true random number generator based on timing jitter. + +Note that this RNG is not suited for use cases where cryptographic security is +required (also see [this +discussion](https://github.com/rust-random/rand/issues/699)). + +This crate depends on [rand_core](https://crates.io/crates/rand_core) and is +part of the [Rand project](https://github.com/rust-random/rand). + +This crate aims to support all of Rust's `std` platforms with a system-provided +entropy source. Unlike other Rand crates, this crate does not support `no_std` +(handling this gracefully is a current discussion topic). + +Links: + +- [API documentation (master)](https://rust-random.github.io/rand/rand_jitter) +- [API documentation (docs.rs)](https://docs.rs/rand_jitter) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_jitter/CHANGELOG.md) + +## Features + +This crate has optional `std` support which is *disabled by default*; +this feature is required to provide the `JitterRng::new` function; +without `std` support a timer must be supplied via `JitterRng::new_with_timer`. + +## Quality testing + +`JitterRng::new()` has build-in, but limited, quality testing, however +before using `JitterRng` on untested hardware, or after changes that could +effect how the code is optimized (such as a new LLVM version), it is +recommend to run the much more stringent +[NIST SP 800-90B Entropy Estimation Suite](https://github.com/usnistgov/SP800-90B_EntropyAssessment). + +Use the following code using `timer_stats` to collect the data: + +```rust,no_run +use rand_jitter::JitterRng; + +use std::error::Error; +use std::fs::File; +use std::io::Write; + +fn get_nstime() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + + let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + // The correct way to calculate the current time is + // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` + // But this is faster, and the difference in terms of entropy is + // negligible (log2(10^9) == 29.9). + dur.as_secs() << 30 | dur.subsec_nanos() as u64 +} + +fn main() -> Result<(), Box<dyn Error>> { + let mut rng = JitterRng::new_with_timer(get_nstime); + + // 1_000_000 results are required for the + // NIST SP 800-90B Entropy Estimation Suite + const ROUNDS: usize = 1_000_000; + let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS); + let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS); + + for _ in 0..ROUNDS { + deltas_variable.push(rng.timer_stats(true) as u8); + deltas_minimal.push(rng.timer_stats(false) as u8); + } + + // Write out after the statistics collection loop, to not disturb the + // test results. + File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; + File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; + Ok(()) +} +``` + +This will produce two files: `jitter_rng_var.bin` and `jitter_rng_min.bin`. +Run the Entropy Estimation Suite in three configurations, as outlined below. +Every run has two steps. One step to produce an estimation, another to +validate the estimation. + +1. Estimate the expected amount of entropy that is at least available with + each round of the entropy collector. This number should be greater than + the amount estimated with `64 / test_timer()`. + ```sh + python noniid_main.py -v jitter_rng_var.bin 8 + restart.py -v jitter_rng_var.bin 8 <min-entropy> + ``` +2. Estimate the expected amount of entropy that is available in the last 4 + bits of the timer delta after running noice sources. Note that a value of + `3.70` is the minimum estimated entropy for true randomness. + ```sh + python noniid_main.py -v -u 4 jitter_rng_var.bin 4 + restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy> + ``` +3. Estimate the expected amount of entropy that is available to the entropy + collector if both noise sources only run their minimal number of times. + This measures the absolute worst-case, and gives a lower bound for the + available entropy. + ```sh + python noniid_main.py -v -u 4 jitter_rng_min.bin 4 + restart.py -v -u 4 jitter_rng_min.bin 4 <min-entropy> + ``` + +## License + +`rand_jitter` is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and +[COPYRIGHT](COPYRIGHT) for details. diff --git a/rand/rand_jitter/benches/mod.rs b/rand/rand_jitter/benches/mod.rs new file mode 100644 index 0000000..bf7c8a2 --- /dev/null +++ b/rand/rand_jitter/benches/mod.rs @@ -0,0 +1,17 @@ +#![feature(test)] +#![cfg(std)] + +use test::Bencher; +use rand_jitter::rand_core::RngCore; + +#[bench] +fn bench_add_two(b: &mut Bencher) { + let mut rng = rand_jitter::JitterRng::new().unwrap(); + let mut buf = [0u8; 1024]; + b.iter(|| { + rng.fill_bytes(&mut buf[..]); + test::black_box(&buf); + }); + b.bytes = buf.len() as u64; +} + diff --git a/rand/rand_jitter/src/error.rs b/rand/rand_jitter/src/error.rs new file mode 100644 index 0000000..b54fffa --- /dev/null +++ b/rand/rand_jitter/src/error.rs @@ -0,0 +1,77 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2013-2015 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rand_core::Error; +use core::fmt; + +/// Base code for all `JitterRng` errors +const ERROR_BASE: u32 = 0xAE53_0400; + +/// An error that can occur when [`JitterRng::test_timer`] fails. +/// +/// All variants have a value of 0xAE530400 = 2924676096 plus a small +/// increment (1 through 5). +/// +/// [`JitterRng::test_timer`]: crate::JitterRng::test_timer +#[derive(Debug, Clone, PartialEq, Eq)] +#[repr(u32)] +pub enum TimerError { + /// No timer available. + NoTimer = ERROR_BASE + 1, + /// Timer too coarse to use as an entropy source. + CoarseTimer = ERROR_BASE + 2, + /// Timer is not monotonically increasing. + NotMonotonic = ERROR_BASE + 3, + /// Variations of deltas of time too small. + TinyVariantions = ERROR_BASE + 4, + /// Too many stuck results (indicating no added entropy). + TooManyStuck = ERROR_BASE + 5, + #[doc(hidden)] + __Nonexhaustive, +} + +impl TimerError { + fn description(&self) -> &'static str { + match *self { + TimerError::NoTimer => "no timer available", + TimerError::CoarseTimer => "coarse timer", + TimerError::NotMonotonic => "timer not monotonic", + TimerError::TinyVariantions => "time delta variations too small", + TimerError::TooManyStuck => "too many stuck results", + TimerError::__Nonexhaustive => unreachable!(), + } + } +} + +impl fmt::Display for TimerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +#[cfg(feature = "std")] +impl ::std::error::Error for TimerError { + fn description(&self) -> &str { + self.description() + } +} + +impl From<TimerError> for Error { + fn from(err: TimerError) -> Error { + // Timer check is already quite permissive of failures so we don't + // expect false-positive failures, i.e. any error is irrecoverable. + #[cfg(feature = "std")] { + Error::new(err) + } + #[cfg(not(feature = "std"))] { + Error::from(core::num::NonZeroU32::new(err as u32).unwrap()) + } + } +} + diff --git a/rand/src/rngs/jitter.rs b/rand/rand_jitter/src/lib.rs index 3e93477..49c53e6 100644 --- a/rand/src/rngs/jitter.rs +++ b/rand/rand_jitter/src/lib.rs @@ -13,17 +13,105 @@ // the MIT license. //! Non-physical true random number generator based on timing jitter. +//! +//! Note that this RNG is not suited for use cases where cryptographic security is +//! required (also see this [discussion]). +//! +//! This is a true random number generator, as opposed to pseudo-random +//! generators. Random numbers generated by `JitterRng` can be seen as fresh +//! entropy. A consequence is that it is orders of magnitude slower than `OsRng` +//! and PRNGs (about 10<sup>3</sup>..10<sup>6</sup> slower). +//! +//! There are very few situations where using this RNG is appropriate. Only very +//! few applications require true entropy. A normal PRNG can be statistically +//! indistinguishable, and a cryptographic PRNG should also be as impossible to +//! predict. +//! +//! `JitterRng` can be used without the standard library, but not conveniently, +//! you must provide a high-precision timer and carefully have to follow the +//! instructions of [`JitterRng::new_with_timer`]. +//! +//! This implementation is based on [Jitterentropy] version 2.1.0. +//! +//! Note: There is no accurate timer available on WASM platforms, to help +//! prevent fingerprinting or timing side-channel attacks. Therefore +//! [`JitterRng::new()`] is not available on WASM. It is also unavailable +//! with disabled `std` feature. +//! +//! [Jitterentropy]: http://www.chronox.de/jent.html +//! [discussion]: https://github.com/rust-random/rand/issues/699 + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://rust-random.github.io/rand/")] + +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![doc(test(attr(allow(unused_variables), deny(warnings))))] // Note: the C implementation of `Jitterentropy` relies on being compiled // without optimizations. This implementation goes through lengths to make the // compiler not optimize out code which does influence timing jitter, but is // technically dead code. +#![no_std] +#[cfg(feature = "std")] +extern crate std; -use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls}; +pub use rand_core; + +// Coming from https://crates.io/crates/doc-comment +#[cfg(test)] +macro_rules! doc_comment { + ($x:expr) => { + #[doc = $x] + extern {} + }; +} + +#[cfg(test)] +doc_comment!(include_str!("../README.md")); + +#[allow(unused)] +macro_rules! trace { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::trace!($($x)*) + } +) } +#[allow(unused)] +macro_rules! debug { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::debug!($($x)*) + } +) } +#[allow(unused)] +macro_rules! info { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::info!($($x)*) + } +) } +#[allow(unused)] +macro_rules! warn { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::warn!($($x)*) + } +) } +#[allow(unused)] +macro_rules! error { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::error!($($x)*) + } +) } + +#[cfg(feature = "std")] +mod platform; +mod error; + +use rand_core::{RngCore, Error, impls}; +pub use crate::error::TimerError; use core::{fmt, mem, ptr}; -#[cfg(all(feature="std", not(target_arch = "wasm32")))] -use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; +#[cfg(feature = "std")] +use std::sync::atomic::{AtomicUsize, Ordering}; const MEMORY_BLOCKS: usize = 64; const MEMORY_BLOCKSIZE: usize = 32; @@ -32,107 +120,8 @@ const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; /// A true random number generator based on jitter in the CPU execution time, /// and jitter in memory access time. /// -/// This is a true random number generator, as opposed to pseudo-random -/// generators. Random numbers generated by `JitterRng` can be seen as fresh -/// entropy. A consequence is that is orders of magnitude slower than [`OsRng`] -/// and PRNGs (about 10<sup>3</sup>..10<sup>6</sup> slower). -/// -/// There are very few situations where using this RNG is appropriate. Only very -/// few applications require true entropy. A normal PRNG can be statistically -/// indistinguishable, and a cryptographic PRNG should also be as impossible to -/// predict. -/// -/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when -/// [`OsRng`] is not available. -/// -/// `JitterRng` can be used without the standard library, but not conveniently, -/// you must provide a high-precision timer and carefully have to follow the -/// instructions of [`new_with_timer`]. -/// -/// This implementation is based on -/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0. -/// -/// Note: There is no accurate timer available on Wasm platforms, to help -/// prevent fingerprinting or timing side-channel attacks. Therefore -/// [`JitterRng::new()`] is not available on Wasm. -/// -/// # Quality testing -/// -/// [`JitterRng::new()`] has build-in, but limited, quality testing, however -/// before using `JitterRng` on untested hardware, or after changes that could -/// effect how the code is optimized (such as a new LLVM version), it is -/// recommend to run the much more stringent -/// [NIST SP 800-90B Entropy Estimation Suite]( -/// https://github.com/usnistgov/SP800-90B_EntropyAssessment). -/// -/// Use the following code using [`timer_stats`] to collect the data: -/// -/// ```no_run -/// use rand::rngs::JitterRng; -/// # -/// # use std::error::Error; -/// # use std::fs::File; -/// # use std::io::Write; -/// # -/// # fn try_main() -> Result<(), Box<Error>> { -/// let mut rng = JitterRng::new()?; -/// -/// // 1_000_000 results are required for the -/// // NIST SP 800-90B Entropy Estimation Suite -/// const ROUNDS: usize = 1_000_000; -/// let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS); -/// let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS); -/// -/// for _ in 0..ROUNDS { -/// deltas_variable.push(rng.timer_stats(true) as u8); -/// deltas_minimal.push(rng.timer_stats(false) as u8); -/// } -/// -/// // Write out after the statistics collection loop, to not disturb the -/// // test results. -/// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; -/// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; -/// # -/// # Ok(()) -/// # } -/// # -/// # fn main() { -/// # try_main().unwrap(); -/// # } -/// ``` -/// -/// This will produce two files: `jitter_rng_var.bin` and `jitter_rng_min.bin`. -/// Run the Entropy Estimation Suite in three configurations, as outlined below. -/// Every run has two steps. One step to produce an estimation, another to -/// validate the estimation. -/// -/// 1. Estimate the expected amount of entropy that is at least available with -/// each round of the entropy collector. This number should be greater than -/// the amount estimated with `64 / test_timer()`. -/// ```sh -/// python noniid_main.py -v jitter_rng_var.bin 8 -/// restart.py -v jitter_rng_var.bin 8 <min-entropy> -/// ``` -/// 2. Estimate the expected amount of entropy that is available in the last 4 -/// bits of the timer delta after running noice sources. Note that a value of -/// `3.70` is the minimum estimated entropy for true randomness. -/// ```sh -/// python noniid_main.py -v -u 4 jitter_rng_var.bin 4 -/// restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy> -/// ``` -/// 3. Estimate the expected amount of entropy that is available to the entropy -/// collector if both noice sources only run their minimal number of times. -/// This measures the absolute worst-case, and gives a lower bound for the -/// available entropy. -/// ```sh -/// python noniid_main.py -v -u 4 jitter_rng_min.bin 4 -/// restart.py -v -u 4 jitter_rng_min.bin 4 <min-entropy> -/// ``` -/// -/// [`OsRng`]: struct.OsRng.html -/// [`JitterRng::new()`]: struct.JitterRng.html#method.new -/// [`new_with_timer`]: struct.JitterRng.html#method.new_with_timer -/// [`timer_stats`]: struct.JitterRng.html#method.timer_stats +/// Note that this RNG is not suitable for use cases where cryptographic +/// security is required. pub struct JitterRng { data: u64, // Actual random number // Number of rounds to run the entropy collector per 64 bits @@ -215,63 +204,9 @@ impl Clone for JitterRng { } } -/// An error that can occur when [`JitterRng::test_timer`] fails. -/// -/// [`JitterRng::test_timer`]: struct.JitterRng.html#method.test_timer -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TimerError { - /// No timer available. - NoTimer, - /// Timer too coarse to use as an entropy source. - CoarseTimer, - /// Timer is not monotonically increasing. - NotMonotonic, - /// Variations of deltas of time too small. - TinyVariantions, - /// Too many stuck results (indicating no added entropy). - TooManyStuck, - #[doc(hidden)] - __Nonexhaustive, -} - -impl TimerError { - fn description(&self) -> &'static str { - match *self { - TimerError::NoTimer => "no timer available", - TimerError::CoarseTimer => "coarse timer", - TimerError::NotMonotonic => "timer not monotonic", - TimerError::TinyVariantions => "time delta variations too small", - TimerError::TooManyStuck => "too many stuck results", - TimerError::__Nonexhaustive => unreachable!(), - } - } -} - -impl fmt::Display for TimerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -#[cfg(feature="std")] -impl ::std::error::Error for TimerError { - fn description(&self) -> &str { - self.description() - } -} - -impl From<TimerError> for Error { - fn from(err: TimerError) -> Error { - // Timer check is already quite permissive of failures so we don't - // expect false-positive failures, i.e. any error is irrecoverable. - Error::with_cause(ErrorKind::Unavailable, - "timer jitter failed basic quality tests", err) - } -} - // Initialise to zero; must be positive -#[cfg(all(feature="std", not(target_arch = "wasm32")))] -static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; +#[cfg(all(feature = "std", not(target_arch = "wasm32")))] +static JITTER_ROUNDS: AtomicUsize = AtomicUsize::new(0); impl JitterRng { /// Create a new `JitterRng`. Makes use of `std::time` for a timer, or a @@ -281,8 +216,11 @@ impl JitterRng { /// During initialization CPU execution timing jitter is measured a few /// hundred times. If this does not pass basic quality tests, an error is /// returned. The test result is cached to make subsequent calls faster. - #[cfg(all(feature="std", not(target_arch = "wasm32")))] + #[cfg(all(feature = "std", not(target_arch = "wasm32")))] pub fn new() -> Result<JitterRng, TimerError> { + if cfg!(target_arch = "wasm32") { + return Err(TimerError::NoTimer); + } let mut state = JitterRng::new_with_timer(platform::get_nstime); let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u8; if rounds == 0 { @@ -314,8 +252,8 @@ impl JitterRng { /// # Example /// /// ``` - /// # use rand::{Rng, Error}; - /// use rand::rngs::JitterRng; + /// # use rand_jitter::rand_core::{RngCore, Error}; + /// use rand_jitter::JitterRng; /// /// # fn try_inner() -> Result<(), Error> { /// fn get_nstime() -> u64 { @@ -332,18 +270,18 @@ impl JitterRng { /// let mut rng = JitterRng::new_with_timer(get_nstime); /// let rounds = rng.test_timer()?; /// rng.set_rounds(rounds); // optional - /// let _ = rng.gen::<u64>(); + /// let _ = rng.next_u64(); /// /// // Ready for use - /// let v: u64 = rng.gen(); + /// let v: u64 = rng.next_u64(); /// # Ok(()) /// # } /// /// # let _ = try_inner(); /// ``` /// - /// [`test_timer`]: struct.JitterRng.html#method.test_timer - /// [`set_rounds`]: struct.JitterRng.html#method.set_rounds + /// [`test_timer`]: JitterRng::test_timer + /// [`set_rounds`]: JitterRng::set_rounds pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { JitterRng { data: 0, @@ -363,7 +301,7 @@ impl JitterRng { /// rounds required for full strength (platform dependent), so one may use /// `rng.set_rounds(rng.test_timer()?);` or cache the value. /// - /// [`new_with_timer`]: struct.JitterRng.html#method.new_with_timer + /// [`new_with_timer`]: JitterRng::new_with_timer pub fn set_rounds(&mut self, rounds: u8) { assert!(rounds > 0); self.rounds = rounds; @@ -600,15 +538,13 @@ impl JitterRng { self.stir_pool(); self.data } - + /// Basic quality tests on the timer, by measuring CPU timing jitter a few /// hundred times. /// - /// If succesful, this will return the estimated number of rounds necessary + /// If successful, this will return the estimated number of rounds necessary /// to collect 64 bits of entropy. Otherwise a [`TimerError`] with the cause /// of the failure will be returned. - /// - /// [`TimerError`]: enum.TimerError.html pub fn test_timer(&mut self) -> Result<u8, TimerError> { debug!("JitterRng: testing timer ..."); // We could add a check for system capabilities such as `clock_getres` @@ -758,8 +694,8 @@ impl JitterRng { /// of entropy one round of the entropy collector can collect in the worst /// case. /// - /// See [Quality testing](struct.JitterRng.html#quality-testing) on how to - /// use `timer_stats` to test the quality of `JitterRng`. + /// See this crate's README on how to use `timer_stats` to test the quality + /// of `JitterRng`. pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { let mut mem = [0; MEMORY_SIZE]; @@ -771,45 +707,6 @@ impl JitterRng { } } -#[cfg(feature="std")] -mod platform { - #[cfg(not(any(target_os = "macos", target_os = "ios", - target_os = "windows", - target_arch = "wasm32")))] - pub fn get_nstime() -> u64 { - use std::time::{SystemTime, UNIX_EPOCH}; - - let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - // The correct way to calculate the current time is - // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` - // But this is faster, and the difference in terms of entropy is - // negligible (log2(10^9) == 29.9). - dur.as_secs() << 30 | dur.subsec_nanos() as u64 - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - pub fn get_nstime() -> u64 { - extern crate libc; - // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution. - // We use `mach_absolute_time` instead. This provides a CPU dependent - // unit, to get real nanoseconds the result should by multiplied by - // numer/denom from `mach_timebase_info`. - // But we are not interested in the exact nanoseconds, just entropy. So - // we use the raw result. - unsafe { libc::mach_absolute_time() } - } - - #[cfg(target_os = "windows")] - pub fn get_nstime() -> u64 { - extern crate winapi; - unsafe { - let mut t = super::mem::zeroed(); - winapi::um::profileapi::QueryPerformanceCounter(&mut t); - *t.QuadPart() as u64 - } - } -} - // A function that is opaque to the optimizer to assist in avoiding dead-code // elimination. Taken from `bencher`. fn black_box<T>(dummy: T) -> T { @@ -851,35 +748,3 @@ impl RngCore for JitterRng { Ok(self.fill_bytes(dest)) } } - -impl CryptoRng for JitterRng {} - -#[cfg(test)] -mod test_jitter_init { - use super::JitterRng; - - #[cfg(all(feature="std", not(target_arch = "wasm32")))] - #[test] - fn test_jitter_init() { - use RngCore; - // Because this is a debug build, measurements here are not representive - // of the final release build. - // Don't fail this test if initializing `JitterRng` fails because of a - // bad timer (the timer from the standard library may not have enough - // accuracy on all platforms). - match JitterRng::new() { - Ok(ref mut rng) => { - // false positives are possible, but extremely unlikely - assert!(rng.next_u32() | rng.next_u32() != 0); - }, - Err(_) => {}, - } - } - - #[test] - fn test_jitter_bad_timer() { - fn bad_timer() -> u64 { 0 } - let mut rng = JitterRng::new_with_timer(bad_timer); - assert!(rng.test_timer().is_err()); - } -} diff --git a/rand/rand_jitter/src/platform.rs b/rand/rand_jitter/src/platform.rs new file mode 100644 index 0000000..8e3d0fb --- /dev/null +++ b/rand/rand_jitter/src/platform.rs @@ -0,0 +1,44 @@ +// Copyright 2018 Developers of the Rand project. +// Copyright 2013-2015 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))] +pub fn get_nstime() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + + let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + // The correct way to calculate the current time is + // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64` + // But this is faster, and the difference in terms of entropy is + // negligible (log2(10^9) == 29.9). + dur.as_secs() << 30 | dur.subsec_nanos() as u64 +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn get_nstime() -> u64 { + use libc; + + // On Mac OS and iOS std::time::SystemTime only has 1000ns resolution. + // We use `mach_absolute_time` instead. This provides a CPU dependent + // unit, to get real nanoseconds the result should by multiplied by + // numer/denom from `mach_timebase_info`. + // But we are not interested in the exact nanoseconds, just entropy. So + // we use the raw result. + unsafe { libc::mach_absolute_time() } +} + +#[cfg(target_os = "windows")] +pub fn get_nstime() -> u64 { + use winapi; + + unsafe { + let mut t = super::mem::zeroed(); + winapi::um::profileapi::QueryPerformanceCounter(&mut t); + *t.QuadPart() as u64 + } +} diff --git a/rand/rand_jitter/tests/mod.rs b/rand/rand_jitter/tests/mod.rs new file mode 100644 index 0000000..961dc27 --- /dev/null +++ b/rand/rand_jitter/tests/mod.rs @@ -0,0 +1,28 @@ +use rand_jitter::JitterRng; +#[cfg(feature = "std")] +use rand_core::RngCore; + +#[cfg(feature = "std")] +#[test] +fn test_jitter_init() { + // Because this is a debug build, measurements here are not representive + // of the final release build. + // Don't fail this test if initializing `JitterRng` fails because of a + // bad timer (the timer from the standard library may not have enough + // accuracy on all platforms). + match JitterRng::new() { + Ok(ref mut rng) => { + // false positives are possible, but extremely unlikely + assert!(rng.next_u32() | rng.next_u32() != 0); + }, + Err(_) => {}, + } +} + +#[test] +fn test_jitter_bad_timer() { + fn bad_timer() -> u64 { 0 } + let mut rng = JitterRng::new_with_timer(bad_timer); + assert!(rng.test_timer().is_err()); +} + diff --git a/rand/rand_os/CHANGELOG.md b/rand/rand_os/CHANGELOG.md index 459f7bd..b0c6549 100644 --- a/rand/rand_os/CHANGELOG.md +++ b/rand/rand_os/CHANGELOG.md @@ -4,9 +4,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.2] - 2019-09-02 +### Changed +- `OsRng` added to `rand_core`, rendering this crate deprecated (#863) + +## [0.2.1] - 2019-08-08 +### Fixed +- Fix `no_std` support. + +## [0.2.0] - 2019-06-06 +### Changed +- Minimum Supported Rust Version has changed to 1.32. +- Replaced implementation with a backwards-compatible shim around +[getrandom](https://crates.io/crates/getrandom). + +## [0.1.3] - 2019-03-05 +### Fixed +- Fix support for Illumos (#730) +- Fix deprecation warnings from atomic init (#739) + +## [0.1.2] - 2019-01-28 +### Changed +- Fuchsia: Replaced fuchsia-zircon with fuchsia-cprng ## [0.1.1] - 2019-01-08 -### Additions +### Added - Add support for x86_64-fortanix-unknown-sgx target (#670) ## [0.1.0] - 2019-01-04 diff --git a/rand/rand_os/Cargo.toml b/rand/rand_os/Cargo.toml index 2f9224a..c8010e2 100644 --- a/rand/rand_os/Cargo.toml +++ b/rand/rand_os/Cargo.toml @@ -1,38 +1,26 @@ [package] name = "rand_os" -version = "0.1.1" +version = "0.2.2" authors = ["The Rand Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" documentation = "https://docs.rs/rand_os" homepage = "https://crates.io/crates/rand_os" description = "OS backed Random Number Generator" keywords = ["random", "rng", "os"] +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } -[dependencies] -rand_core = { path = "../rand_core", version = "0.3", default-features = false } -log = { version = "0.4", optional = true } - -[target.'cfg(unix)'.dependencies] -libc = "0.2" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "winnt"] } - -[target.'cfg(target_os = "cloudabi")'.dependencies] -cloudabi = "0.0.3" +[features] +log = ["getrandom/log"] +# re-export optional WASM dependencies to avoid breakage: +wasm-bindgen = ["getrandom/wasm-bindgen"] +stdweb = ["getrandom/stdweb"] -[target.'cfg(target_os = "fuchsia")'.dependencies] -fuchsia-zircon = "0.3.2" - -[target.wasm32-unknown-unknown.dependencies] -wasm-bindgen = { version = "0.2.12", optional = true } -stdweb = { version = "0.4", optional = true } - -[target.'cfg(target_env = "sgx")'.dependencies] -rdrand = "0.4.0" +[dependencies] +rand_core = { path = "../rand_core", version = "0.5", features = ["getrandom"] } +getrandom = "0.1.1" diff --git a/rand/rand_os/README.md b/rand/rand_os/README.md index 4f48b63..7b68b35 100644 --- a/rand/rand_os/README.md +++ b/rand/rand_os/README.md @@ -6,23 +6,25 @@ [![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_os) [![API](https://docs.rs/rand_os/badge.svg)](https://docs.rs/rand_os) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) A random number generator that retrieves randomness straight from the operating system. -This crate depends on [rand_core](https://crates.io/crates/rand_core) and is -part of the [Rand project](https://github.com/rust-random/rand). +**This crate is deprecated:** `OsRng` is available in `rand_core` since version 0.5.1. -This crate aims to support all of Rust's `std` platforms with a system-provided -entropy source. Unlike other Rand crates, this crate does not support `no_std` -(handling this gracefully is a current discussion topic). +This crate provides `OsRng` as a shim around +[getrandom](https://crates.io/crates/getrandom) +implementing `RngCore` from [rand_core](https://crates.io/crates/rand_core). + +Note: the `rand` crate provides an equivalent `OsRng`; the two implementations +are equivalent, though distinct types. Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_os) - [API documentation (docs.rs)](https://docs.rs/rand_os) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_os/CHANGELOG.md) ## License diff --git a/rand/rand_os/src/cloudabi.rs b/rand/rand_os/src/cloudabi.rs deleted file mode 100644 index 8b96a2b..0000000 --- a/rand/rand_os/src/cloudabi.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for CloudABI - -extern crate cloudabi; - -use std::io; -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let errno = unsafe { cloudabi::random_get(dest) }; - if errno == cloudabi::errno::SUCCESS { - Ok(()) - } else { - // Cloudlibc provides its own `strerror` implementation so we - // can use `from_raw_os_error` here. - Err(Error::with_cause( - ErrorKind::Unavailable, - "random_get() system call failed", - io::Error::from_raw_os_error(errno as i32), - )) - } - } - - fn method_str(&self) -> &'static str { "cloudabi::random_get" } -} diff --git a/rand/rand_os/src/dragonfly_haiku_emscripten.rs b/rand/rand_os/src/dragonfly_haiku_emscripten.rs deleted file mode 100644 index 6132d7a..0000000 --- a/rand/rand_os/src/dragonfly_haiku_emscripten.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for DragonFly / Haiku / Emscripten - -use rand_core::Error; -use super::random_device; -use super::OsRngImpl; -use std::fs::File; - -#[derive(Clone, Debug)] -pub struct OsRng(); - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - random_device::open("/dev/random", &|p| File::open(p))?; - Ok(OsRng()) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - random_device::read(dest) - } - - #[cfg(target_os = "emscripten")] - fn max_chunk_size(&self) -> usize { - // `Crypto.getRandomValues` documents `dest` should be at most 65536 - // bytes. `crypto.randomBytes` documents: "To minimize threadpool - // task length variation, partition large randomBytes requests when - // doing so as part of fulfilling a client request. - 65536 - } - - fn method_str(&self) -> &'static str { "/dev/random" } -} diff --git a/rand/rand_os/src/dummy_log.rs b/rand/rand_os/src/dummy_log.rs deleted file mode 100644 index ccfe4ba..0000000 --- a/rand/rand_os/src/dummy_log.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[allow(unused)] -macro_rules! trace { ($($x:tt)*) => () } -#[allow(unused)] -macro_rules! debug { ($($x:tt)*) => () } -#[allow(unused)] -macro_rules! info { ($($x:tt)*) => () } -#[allow(unused)] -macro_rules! warn { ($($x:tt)*) => () } -#[allow(unused)] -macro_rules! error { ($($x:tt)*) => () } diff --git a/rand/rand_os/src/freebsd.rs b/rand/rand_os/src/freebsd.rs deleted file mode 100644 index 6b8e672..0000000 --- a/rand/rand_os/src/freebsd.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for FreeBSD - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::ptr; -use std::io; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let mib = [libc::CTL_KERN, libc::KERN_ARND]; - let mut len = dest.len(); - let ret = unsafe { - libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint, - dest.as_mut_ptr() as *mut _, &mut len, - ptr::null(), 0) - }; - if ret == -1 || len != dest.len() { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "kern.arandom sysctl failed", - io::Error::last_os_error())); - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { 256 } - - fn method_str(&self) -> &'static str { "kern.arandom" } -} diff --git a/rand/rand_os/src/fuchsia.rs b/rand/rand_os/src/fuchsia.rs deleted file mode 100644 index 7063ff6..0000000 --- a/rand/rand_os/src/fuchsia.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Fuchsia Zircon - -extern crate fuchsia_zircon; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let mut read = 0; - while read < dest.len() { - match fuchsia_zircon::cprng_draw(&mut dest[read..]) { - Ok(actual) => read += actual, - Err(e) => { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "cprng_draw failed", - e.into_io_error())); - } - }; - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { - fuchsia_zircon::sys::ZX_CPRNG_DRAW_MAX_LEN - } - - fn method_str(&self) -> &'static str { "cprng_draw" } -} diff --git a/rand/rand_os/src/lib.rs b/rand/rand_os/src/lib.rs index 67b0dfe..abfdf79 100644 --- a/rand/rand_os/src/lib.rs +++ b/rand/rand_os/src/lib.rs @@ -8,118 +8,8 @@ // except according to those terms. //! Interface to the random number generator of the operating system. -//! -//! `OsRng` is the preferred external source of entropy for most applications. -//! Commonly it is used to initialize a user-space RNG, which can then be used -//! to generate random values with much less overhead than `OsRng`. -//! -//! You may prefer to use [`EntropyRng`] instead of `OsRng`. It is unlikely, but -//! not entirely theoretical, for `OsRng` to fail. In such cases [`EntropyRng`] -//! falls back on a good alternative entropy source. -//! -//! `OsRng::new()` is guaranteed to be very cheap (after the first successful -//! call), and will never consume more than one file handle per process. -//! -//! # Usage example -//! ``` -//! use rand_os::OsRng; -//! use rand_os::rand_core::RngCore; -//! -//! let mut os_rng = OsRng::new().unwrap(); -//! let mut key = [0u8; 16]; -//! os_rng.fill_bytes(&mut key); -//! let random_u64 = os_rng.next_u64(); -//! ``` -//! -//! # Platform sources -//! -//! | OS | interface -//! |------------------|--------------------------------------------------------- -//! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once -//! | Windows | [`RtlGenRandom`][3] -//! | macOS, iOS | [`SecRandomCopyBytes`][4] -//! | FreeBSD | [`kern.arandom`][5] -//! | OpenBSD, Bitrig | [`getentropy`][6] -//! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once -//! | Dragonfly BSD | [`/dev/random`][8] -//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10] -//! | Fuchsia OS | [`cprng_draw`][11] -//! | Redox | [`rand:`][12] -//! | CloudABI | [`random_get`][13] -//! | Haiku | `/dev/random` (identical to `/dev/urandom`) -//! | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14]) -//! | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16]) -//! -//! Rand doesn't have a blanket implementation for all Unix-like operating -//! systems that reads from `/dev/urandom`. This ensures all supported operating -//! systems are using the recommended interface and respect maximum buffer -//! sizes. -//! -//! ## Support for WebAssembly and ams.js -//! -//! The three Emscripten targets `asmjs-unknown-emscripten`, -//! `wasm32-unknown-emscripten` and `wasm32-experimental-emscripten` use -//! Emscripten's emulation of `/dev/random` on web browsers and Node.js. -//! -//! The bare WASM target `wasm32-unknown-unknown` tries to call the javascript -//! methods directly, using either `stdweb` or `wasm-bindgen` depending on what -//! features are activated for this crate. Note that if both features are -//! enabled `wasm-bindgen` will be used. -//! -//! ## Early boot -//! -//! It is possible that early in the boot process the OS hasn't had enough time -//! yet to collect entropy to securely seed its RNG, especially on virtual -//! machines. -//! -//! Some operating systems always block the thread until the RNG is securely -//! seeded. This can take anywhere from a few seconds to more than a minute. -//! Others make a best effort to use a seed from before the shutdown and don't -//! document much. -//! -//! A few, Linux, NetBSD and Solaris, offer a choice between blocking, and -//! getting an error. With `try_fill_bytes` we choose to get the error -//! ([`ErrorKind::NotReady`]), while the other methods use a blocking interface. -//! -//! On Linux (when the `genrandom` system call is not available) and on NetBSD -//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected -//! enough entropy yet. As a countermeasure we try to do a single read from -//! `/dev/random` until we know the OS RNG is initialized (and store this in a -//! global static). -//! -//! # Panics and error handling -//! -//! We cannot guarantee that `OsRng` will fail, but if it does, it will likely -//! be either when `OsRng::new()` is first called or when data is first read. -//! If you wish to catch errors early, then test reading of at least one byte -//! from `OsRng` via [`try_fill_bytes`]. If this succeeds, it is extremely -//! unlikely that any further errors will occur. -//! -//! Only [`try_fill_bytes`] is able to report the cause of an error; the other -//! [`RngCore`] methods may (depending on the error kind) retry several times, -//! but must eventually panic if the error persists. -//! -//! [`EntropyRng`]: ../rand/rngs/struct.EntropyRng.html -//! [`RngCore`]: ../rand_core/trait.RngCore.html -//! [`try_fill_bytes`]: ../rand_core/trait.RngCore.html#method.tymethod.try_fill_bytes -//! [`ErrorKind::NotReady`]: ../rand_core/enum.ErrorKind.html#variant.NotReady -//! -//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html -//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html -//! [3]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx -//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc -//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 -//! [6]: https://man.openbsd.org/getentropy.2 -//! [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current -//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random§ion=4 -//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html -//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html -//! [11]: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md -//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs -//! [13]: https://github.com/NuxiNL/cloudabi/blob/v0.20/cloudabi.txt#L1826 -//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues -//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback -//! [16]: #support-for-webassembly-and-amsjs +// Note: keep this code in sync with the rand::rngs::os module! + #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", html_root_url = "https://rust-random.github.io/rand/")] @@ -127,46 +17,54 @@ #![deny(missing_debug_implementations)] #![doc(test(attr(allow(unused_variables), deny(warnings))))] -#![cfg_attr(feature = "stdweb", recursion_limit="128")] - -pub extern crate rand_core; -#[cfg(feature = "log")] -#[macro_use] extern crate log; - -// We have to do it here because we load macros -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten"), - feature = "wasm-bindgen"))] -extern crate wasm_bindgen; -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten"), - not(feature = "wasm-bindgen"), - feature = "stdweb"))] -#[macro_use] extern crate stdweb; +#![no_std] // but see getrandom crate -#[cfg(target_env = "sgx")] -extern crate rdrand; +#![deprecated(since="0.2.2", note="OsRng is now provided by rand_core and rand")] -#[cfg(not(feature = "log"))] -#[macro_use] -mod dummy_log; +pub use rand_core; // re-export -use std::fmt; +use getrandom::getrandom; use rand_core::{CryptoRng, RngCore, Error, impls}; -/// A random number generator that retrieves randomness straight from the +/// A random number generator that retrieves randomness from from the /// operating system. -#[derive(Clone)] -pub struct OsRng(imp::OsRng); - -impl fmt::Debug for OsRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} +/// +/// This is a zero-sized struct. It can be freely constructed with `OsRng`. +/// +/// The implementation is provided by the [getrandom] crate. Refer to +/// [getrandom] documentation for details. +/// +/// # Blocking and error handling +/// +/// It is possible that when used during early boot the first call to `OsRng` +/// will block until the system's RNG is initialised. It is also possible +/// (though highly unlikely) for `OsRng` to fail on some platforms, most +/// likely due to system mis-configuration. +/// +/// After the first successful call, it is highly unlikely that failures or +/// significant delays will occur (although performance should be expected to +/// be much slower than a user-space PRNG). +/// +/// # Usage example +/// ``` +/// #![allow(deprecated)] +/// use rand_os::rand_core::RngCore; +/// use rand_os::OsRng; +/// +/// let mut key = [0u8; 16]; +/// OsRng.fill_bytes(&mut key); +/// let random_u64 = OsRng.next_u64(); +/// ``` +/// +/// [getrandom]: https://crates.io/crates/getrandom +#[derive(Clone, Copy, Debug, Default)] +pub struct OsRng; impl OsRng { /// Create a new `OsRng`. + #[deprecated(since="0.2.0", note="replace OsRng::new().unwrap() with just OsRng")] pub fn new() -> Result<OsRng, Error> { - imp::OsRng::new().map(OsRng) + Ok(OsRng) } } @@ -182,258 +80,27 @@ impl RngCore for OsRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - use std::{time, thread}; - - // We cannot return Err(..), so we try to handle before panicking. - const MAX_RETRY_PERIOD: u32 = 10; // max 10s - const WAIT_DUR_MS: u32 = 100; // retry every 100ms - let wait_dur = time::Duration::from_millis(WAIT_DUR_MS as u64); - const RETRY_LIMIT: u32 = (MAX_RETRY_PERIOD * 1000) / WAIT_DUR_MS; - const TRANSIENT_RETRIES: u32 = 8; - let mut err_count = 0; - let mut error_logged = false; - - // Maybe block until the OS RNG is initialized - let mut read = 0; - if let Ok(n) = self.0.test_initialized(dest, true) { read = n }; - let dest = &mut dest[read..]; - - loop { - if let Err(e) = self.try_fill_bytes(dest) { - if err_count >= RETRY_LIMIT { - error!("OsRng failed too many times; last error: {}", e); - panic!("OsRng failed too many times; last error: {}", e); - } - - if e.kind.should_wait() { - if !error_logged { - warn!("OsRng failed; waiting up to {}s and retrying. Error: {}", - MAX_RETRY_PERIOD, e); - error_logged = true; - } - err_count += 1; - thread::sleep(wait_dur); - continue; - } else if e.kind.should_retry() { - if !error_logged { - warn!("OsRng failed; retrying up to {} times. Error: {}", - TRANSIENT_RETRIES, e); - error_logged = true; - } - err_count += (RETRY_LIMIT + TRANSIENT_RETRIES - 1) - / TRANSIENT_RETRIES; // round up - continue; - } else { - error!("OsRng failed: {}", e); - panic!("OsRng fatal error: {}", e); - } - } - - break; + if let Err(e) = self.try_fill_bytes(dest) { + panic!("Error: {}", e); } } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - // Some systems do not support reading 0 random bytes. - // (And why waste a system call?) - if dest.len() == 0 { return Ok(()); } - - let read = self.0.test_initialized(dest, false)?; - let dest = &mut dest[read..]; - - let max = self.0.max_chunk_size(); - if dest.len() <= max { - trace!("OsRng: reading {} bytes via {}", - dest.len(), self.0.method_str()); - } else { - trace!("OsRng: reading {} bytes via {} in {} chunks of {} bytes", - dest.len(), self.0.method_str(), (dest.len() + max) / max, max); - } - for slice in dest.chunks_mut(max) { - self.0.fill_chunk(slice)?; - } + getrandom(dest)?; Ok(()) } } -trait OsRngImpl where Self: Sized { - // Create a new `OsRng` platform interface. - fn new() -> Result<Self, Error>; - - // Fill a chunk with random bytes. - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error>; - - // Test whether the OS RNG is initialized. This method may not be possible - // to support cheaply (or at all) on all operating systems. - // - // If `blocking` is set, this will cause the OS the block execution until - // its RNG is initialized. - // - // Random values that are read while this are stored in `dest`, the amount - // of read bytes is returned. - fn test_initialized(&mut self, _dest: &mut [u8], _blocking: bool) - -> Result<usize, Error> { Ok(0) } - - // Maximum chunk size supported. - fn max_chunk_size(&self) -> usize { ::std::usize::MAX } - - // Name of the OS interface (used for logging). - fn method_str(&self) -> &'static str; -} - -#[cfg(any(target_os = "linux", target_os = "android", - target_os = "netbsd", target_os = "dragonfly", - target_os = "solaris", target_os = "redox", - target_os = "haiku", target_os = "emscripten"))] -mod random_device; - -macro_rules! mod_use { - ($cond:meta, $module:ident) => { - #[$cond] - mod $module; - #[$cond] - use $module as imp; - } -} - -mod_use!(cfg(target_os = "android"), linux_android); -mod_use!(cfg(target_os = "bitrig"), openbsd_bitrig); -mod_use!(cfg(target_os = "cloudabi"), cloudabi); -mod_use!(cfg(target_os = "dragonfly"), dragonfly_haiku_emscripten); -mod_use!(cfg(target_os = "emscripten"), dragonfly_haiku_emscripten); -mod_use!(cfg(target_os = "freebsd"), freebsd); -mod_use!(cfg(target_os = "fuchsia"), fuchsia); -mod_use!(cfg(target_os = "haiku"), dragonfly_haiku_emscripten); -mod_use!(cfg(target_os = "ios"), macos); -mod_use!(cfg(target_os = "linux"), linux_android); -mod_use!(cfg(target_os = "macos"), macos); -mod_use!(cfg(target_os = "netbsd"), netbsd); -mod_use!(cfg(target_os = "openbsd"), openbsd_bitrig); -mod_use!(cfg(target_os = "redox"), redox); -mod_use!(cfg(target_os = "solaris"), solaris); -mod_use!(cfg(windows), windows); -mod_use!(cfg(target_env = "sgx"), sgx); - -mod_use!( - cfg(all( - target_arch = "wasm32", - not(target_os = "emscripten"), - feature = "wasm-bindgen" - )), - wasm32_bindgen -); - -mod_use!( - cfg(all( - target_arch = "wasm32", - not(target_os = "emscripten"), - not(feature = "wasm-bindgen"), - feature = "stdweb", - )), - wasm32_stdweb -); - -/// Per #678 we use run-time failure where WASM bindings are missing -#[cfg(all( - target_arch = "wasm32", - not(target_os = "emscripten"), - not(feature = "wasm-bindgen"), - not(feature = "stdweb"), -))] -mod imp { - use rand_core::{Error, ErrorKind}; - use super::OsRngImpl; - - #[derive(Clone, Debug)] - pub struct OsRng; - - impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - Err(Error::new(ErrorKind::Unavailable, - "OsRng: support for wasm32 requires emscripten, stdweb or wasm-bindgen")) - } - - fn fill_chunk(&mut self, _dest: &mut [u8]) -> Result<(), Error> { - unimplemented!() - } - - fn method_str(&self) -> &'static str { unimplemented!() } - } +#[test] +fn test_os_rng() { + let x = OsRng.next_u64(); + let y = OsRng.next_u64(); + assert!(x != 0); + assert!(x != y); } -#[cfg(not(any( - target_os = "android", - target_os = "bitrig", - target_os = "cloudabi", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "haiku", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - windows, - target_arch = "wasm32", - target_env = "sgx" -)))] -compile_error!("OS RNG support is not available for this platform"); - -// Due to rustwasm/wasm-bindgen#201 this can't be defined in the inner os -// modules, so hack around it for now and place it at the root. -#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub mod __wbg_shims { - - // `extern { type Foo; }` isn't supported on 1.22 syntactically, so use a - // macro to work around that. - macro_rules! rust_122_compat { - ($($t:tt)*) => ($($t)*) - } - - rust_122_compat! { - extern crate wasm_bindgen; - - pub use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - extern "C" { - pub type Function; - #[wasm_bindgen(constructor)] - pub fn new(s: &str) -> Function; - #[wasm_bindgen(method)] - pub fn call(this: &Function, self_: &JsValue) -> JsValue; - - pub type This; - #[wasm_bindgen(method, getter, structural, js_name = self)] - pub fn self_(me: &This) -> JsValue; - #[wasm_bindgen(method, getter, structural)] - pub fn crypto(me: &This) -> JsValue; - - #[derive(Clone, Debug)] - pub type BrowserCrypto; - - // TODO: these `structural` annotations here ideally wouldn't be here to - // avoid a JS shim, but for now with feature detection they're - // unavoidable. - #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)] - pub fn get_random_values_fn(me: &BrowserCrypto) -> JsValue; - #[wasm_bindgen(method, js_name = getRandomValues, structural)] - pub fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]); - - #[wasm_bindgen(js_name = require)] - pub fn node_require(s: &str) -> NodeCrypto; - - #[derive(Clone, Debug)] - pub type NodeCrypto; - - #[wasm_bindgen(method, js_name = randomFillSync, structural)] - pub fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]); - } - } +#[test] +fn test_construction() { + let mut rng = OsRng::default(); + assert!(rng.next_u64() != 0); } diff --git a/rand/rand_os/src/linux_android.rs b/rand/rand_os/src/linux_android.rs deleted file mode 100644 index 9622f93..0000000 --- a/rand/rand_os/src/linux_android.rs +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Linux / Android - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::random_device; -use super::OsRngImpl; - -use std::io; -use std::io::Read; -use std::fs::{File, OpenOptions}; -use std::os::unix::fs::OpenOptionsExt; -use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; -use std::sync::{Once, ONCE_INIT}; - -#[derive(Clone, Debug)] -pub struct OsRng { - method: OsRngMethod, - initialized: bool, -} - -#[derive(Clone, Debug)] -enum OsRngMethod { - GetRandom, - RandomDevice, -} - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - if is_getrandom_available() { - return Ok(OsRng { method: OsRngMethod::GetRandom, - initialized: false }); - } - random_device::open("/dev/urandom", &|p| File::open(p))?; - Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - match self.method { - OsRngMethod::GetRandom => getrandom_try_fill(dest, false), - OsRngMethod::RandomDevice => random_device::read(dest), - } - } - - fn test_initialized(&mut self, dest: &mut [u8], blocking: bool) - -> Result<usize, Error> - { - static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; - if !self.initialized { - self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed); - } - if self.initialized { return Ok(0); } - - let result = match self.method { - OsRngMethod::GetRandom => { - getrandom_try_fill(dest, blocking)?; - Ok(dest.len()) - } - OsRngMethod::RandomDevice => { - info!("OsRng: testing random device /dev/random"); - let mut file = OpenOptions::new() - .read(true) - .custom_flags(if blocking { 0 } else { libc::O_NONBLOCK }) - .open("/dev/random") - .map_err(random_device::map_err)?; - file.read(&mut dest[..1]).map_err(random_device::map_err)?; - Ok(1) - } - }; - OS_RNG_INITIALIZED.store(true, Ordering::Relaxed); - self.initialized = true; - result - } - - fn method_str(&self) -> &'static str { - match self.method { - OsRngMethod::GetRandom => "getrandom", - OsRngMethod::RandomDevice => "/dev/urandom", - } - } -} - -#[cfg(target_arch = "x86_64")] -const NR_GETRANDOM: libc::c_long = 318; -#[cfg(target_arch = "x86")] -const NR_GETRANDOM: libc::c_long = 355; -#[cfg(target_arch = "arm")] -const NR_GETRANDOM: libc::c_long = 384; -#[cfg(target_arch = "aarch64")] -const NR_GETRANDOM: libc::c_long = 278; - #[cfg(target_arch = "s390x")] -const NR_GETRANDOM: libc::c_long = 349; -#[cfg(target_arch = "powerpc")] -const NR_GETRANDOM: libc::c_long = 359; -#[cfg(target_arch = "powerpc64")] -const NR_GETRANDOM: libc::c_long = 359; -#[cfg(target_arch = "mips")] // old ABI -const NR_GETRANDOM: libc::c_long = 4353; -#[cfg(target_arch = "mips64")] -const NR_GETRANDOM: libc::c_long = 5313; -#[cfg(target_arch = "sparc")] -const NR_GETRANDOM: libc::c_long = 347; -#[cfg(target_arch = "sparc64")] -const NR_GETRANDOM: libc::c_long = 347; -#[cfg(not(any(target_arch = "x86_64", target_arch = "x86", - target_arch = "arm", target_arch = "aarch64", - target_arch = "s390x", target_arch = "powerpc", - target_arch = "powerpc64", target_arch = "mips", - target_arch = "mips64", target_arch = "sparc", - target_arch = "sparc64")))] -const NR_GETRANDOM: libc::c_long = 0; - -fn getrandom(buf: &mut [u8], blocking: bool) -> libc::c_long { - const GRND_NONBLOCK: libc::c_uint = 0x0001; - - if NR_GETRANDOM == 0 { return -1 }; - - unsafe { - libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), - if blocking { 0 } else { GRND_NONBLOCK }) - } -} - -fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> { - let mut read = 0; - while read < dest.len() { - let result = getrandom(&mut dest[read..], blocking); - if result == -1 { - let err = io::Error::last_os_error(); - let kind = err.kind(); - if kind == io::ErrorKind::Interrupted { - continue; - } else if kind == io::ErrorKind::WouldBlock { - return Err(Error::with_cause( - ErrorKind::NotReady, - "getrandom not ready", - err, - )); - } else { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "unexpected getrandom error", - err, - )); - } - } else { - read += result as usize; - } - } - Ok(()) -} - -fn is_getrandom_available() -> bool { - static CHECKER: Once = ONCE_INIT; - static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT; - - if NR_GETRANDOM == 0 { return false }; - - CHECKER.call_once(|| { - debug!("OsRng: testing getrandom"); - let mut buf: [u8; 0] = []; - let result = getrandom(&mut buf, false); - let available = if result == -1 { - let err = io::Error::last_os_error().raw_os_error(); - err != Some(libc::ENOSYS) - } else { - true - }; - AVAILABLE.store(available, Ordering::Relaxed); - info!("OsRng: using {}", if available { "getrandom" } else { "/dev/urandom" }); - }); - - AVAILABLE.load(Ordering::Relaxed) -} diff --git a/rand/rand_os/src/macos.rs b/rand/rand_os/src/macos.rs deleted file mode 100644 index 6c67251..0000000 --- a/rand/rand_os/src/macos.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for MacOS / iOS - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::io; -use self::libc::{c_int, size_t}; - -#[derive(Clone, Debug)] -pub struct OsRng; - -enum SecRandom {} - -#[allow(non_upper_case_globals)] -const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom; - -#[link(name = "Security", kind = "framework")] -extern { - fn SecRandomCopyBytes(rnd: *const SecRandom, - count: size_t, bytes: *mut u8) -> c_int; -} - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { - SecRandomCopyBytes(kSecRandomDefault, - dest.len() as size_t, - dest.as_mut_ptr()) - }; - if ret == -1 { - Err(Error::with_cause( - ErrorKind::Unavailable, - "couldn't generate random bytes", - io::Error::last_os_error())) - } else { - Ok(()) - } - } - - fn method_str(&self) -> &'static str { "SecRandomCopyBytes" } -} diff --git a/rand/rand_os/src/netbsd.rs b/rand/rand_os/src/netbsd.rs deleted file mode 100644 index cf4b6c7..0000000 --- a/rand/rand_os/src/netbsd.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for NetBSD - -use rand_core::Error; -use super::random_device; -use super::OsRngImpl; - -use std::fs::File; -use std::io::Read; -use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; - -#[derive(Clone, Debug)] -pub struct OsRng { initialized: bool } - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - random_device::open("/dev/urandom", &|p| File::open(p))?; - Ok(OsRng { initialized: false }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - random_device::read(dest) - } - - // Read a single byte from `/dev/random` to determine if the OS RNG is - // already seeded. NetBSD always blocks if not yet ready. - fn test_initialized(&mut self, dest: &mut [u8], _blocking: bool) - -> Result<usize, Error> - { - static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; - if !self.initialized { - self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed); - } - if self.initialized { return Ok(0); } - - info!("OsRng: testing random device /dev/random"); - let mut file = - File::open("/dev/random").map_err(random_device::map_err)?; - file.read(&mut dest[..1]).map_err(random_device::map_err)?; - - OS_RNG_INITIALIZED.store(true, Ordering::Relaxed); - self.initialized = true; - Ok(1) - } - - fn method_str(&self) -> &'static str { "/dev/urandom" } -} diff --git a/rand/rand_os/src/openbsd_bitrig.rs b/rand/rand_os/src/openbsd_bitrig.rs deleted file mode 100644 index c9b35a6..0000000 --- a/rand/rand_os/src/openbsd_bitrig.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for OpenBSD / Bitrig - -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::io; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { - libc::getentropy(dest.as_mut_ptr() as *mut libc::c_void, dest.len()) - }; - if ret == -1 { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "getentropy failed", - io::Error::last_os_error())); - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { 256 } - - fn method_str(&self) -> &'static str { "getentropy" } -} diff --git a/rand/rand_os/src/random_device.rs b/rand/rand_os/src/random_device.rs deleted file mode 100644 index 5da9194..0000000 --- a/rand/rand_os/src/random_device.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Helper functions to read from a random device such as `/dev/urandom`. -//! -//! All instances use a single internal file handle, to prevent possible -//! exhaustion of file descriptors. -use rand_core::{Error, ErrorKind}; -use std::fs::File; -use std::io; -use std::io::Read; -use std::sync::{Once, Mutex, ONCE_INIT}; - -// TODO: remove outer Option when `Mutex::new(None)` is a constant expression -static mut READ_RNG_FILE: Option<Mutex<Option<File>>> = None; -static READ_RNG_ONCE: Once = ONCE_INIT; - -#[allow(unused)] -pub fn open<F>(path: &'static str, open_fn: F) -> Result<(), Error> - where F: Fn(&'static str) -> Result<File, io::Error> -{ - READ_RNG_ONCE.call_once(|| { - unsafe { READ_RNG_FILE = Some(Mutex::new(None)) } - }); - - // We try opening the file outside the `call_once` fn because we cannot - // clone the error, thus we must retry on failure. - - let mutex = unsafe { READ_RNG_FILE.as_ref().unwrap() }; - let mut guard = mutex.lock().unwrap(); - if (*guard).is_none() { - info!("OsRng: opening random device {}", path); - let file = open_fn(path).map_err(map_err)?; - *guard = Some(file); - }; - Ok(()) -} - -pub fn read(dest: &mut [u8]) -> Result<(), Error> { - // We expect this function only to be used after `random_device::open` - // was succesful. Therefore we can assume that our memory was set with a - // valid object. - let mutex = unsafe { READ_RNG_FILE.as_ref().unwrap() }; - let mut guard = mutex.lock().unwrap(); - let file = (*guard).as_mut().unwrap(); - - // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. - file.read_exact(dest).map_err(|err| { - Error::with_cause(ErrorKind::Unavailable, - "error reading random device", err) - }) - -} - -pub fn map_err(err: io::Error) -> Error { - match err.kind() { - io::ErrorKind::Interrupted => - Error::new(ErrorKind::Transient, "interrupted"), - io::ErrorKind::WouldBlock => - Error::with_cause(ErrorKind::NotReady, - "OS RNG not yet seeded", err), - _ => Error::with_cause(ErrorKind::Unavailable, - "error while opening random device", err) - } -} diff --git a/rand/rand_os/src/redox.rs b/rand/rand_os/src/redox.rs deleted file mode 100644 index 36fae26..0000000 --- a/rand/rand_os/src/redox.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Redox - -use rand_core::Error; -use super::random_device; -use super::OsRngImpl; -use std::fs::File; - -#[derive(Clone, Debug)] -pub struct OsRng(); - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - random_device::open("rand:", &|p| File::open(p))?; - Ok(OsRng()) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - random_device::read(dest) - } - - fn method_str(&self) -> &'static str { "'rand:'" } -} diff --git a/rand/rand_os/src/sgx.rs b/rand/rand_os/src/sgx.rs deleted file mode 100644 index 43ae0ef..0000000 --- a/rand/rand_os/src/sgx.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::OsRngImpl; -use Error; -use rdrand::RdRand; -use rand_core::RngCore; -use std::fmt::{Debug, Formatter, Result as FmtResult}; - -#[derive(Clone)] -pub struct OsRng{ - gen: RdRand -} - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - let rng = RdRand::new()?; - Ok(OsRng{ gen: rng }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.gen.try_fill_bytes(dest) - } - - fn method_str(&self) -> &'static str { "RDRAND" } -} - -impl Debug for OsRng { - fn fmt(&self, f: &mut Formatter) -> FmtResult { - f.debug_struct("OsRng") - .finish() - } -} diff --git a/rand/rand_os/src/solaris.rs b/rand/rand_os/src/solaris.rs deleted file mode 100644 index e8965fd..0000000 --- a/rand/rand_os/src/solaris.rs +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for the Solaris family -//! -//! Read from `/dev/random`, with chunks of limited size (1040 bytes). -//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A. -//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less -//! secure. We choose to read from `/dev/random`. -//! -//! Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can -//! compile on both Solaris and on OpenSolaris derivatives, that do not have the -//! function, we do a direct syscall instead of calling a library function. -//! -//! We have no way to differentiate between Solaris, illumos, SmartOS, etc. -extern crate libc; - -use rand_core::{Error, ErrorKind}; -use super::random_device; -use super::OsRngImpl; - -use std::io; -use std::io::Read; -use std::fs::{File, OpenOptions}; -use std::os::unix::fs::OpenOptionsExt; -use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; -use std::cmp; - -#[derive(Clone, Debug)] -pub struct OsRng { - method: OsRngMethod, - initialized: bool, -} - -#[derive(Clone, Debug)] -enum OsRngMethod { - GetRandom, - RandomDevice, -} - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - if is_getrandom_available() { - return Ok(OsRng { method: OsRngMethod::GetRandom, - initialized: false }); - } - let open = |p| OpenOptions::new() - .read(true) - .custom_flags(libc::O_NONBLOCK) - .open(p); - random_device::open("/dev/random", &open)?; - Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false }) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - match self.method { - OsRngMethod::GetRandom => getrandom_try_fill(dest, false), - OsRngMethod::RandomDevice => random_device::read(dest), - } - } - - fn test_initialized(&mut self, dest: &mut [u8], blocking: bool) - -> Result<usize, Error> - { - static OS_RNG_INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT; - if !self.initialized { - self.initialized = OS_RNG_INITIALIZED.load(Ordering::Relaxed); - } - if self.initialized { return Ok(0); } - - let chunk_len = cmp::min(1024, dest.len()); - let dest = &mut dest[..chunk_len]; - - match self.method { - OsRngMethod::GetRandom => getrandom_try_fill(dest, blocking)?, - OsRngMethod::RandomDevice => { - if blocking { - info!("OsRng: testing random device /dev/random"); - // We already have a non-blocking handle, but now need a - // blocking one. Not much choice except opening it twice - let mut file = File::open("/dev/random") - .map_err(random_device::map_err)?; - file.read(dest).map_err(random_device::map_err)?; - } else { - self.fill_chunk(dest)?; - } - } - }; - OS_RNG_INITIALIZED.store(true, Ordering::Relaxed); - self.initialized = true; - Ok(chunk_len) - } - - fn max_chunk_size(&self) -> usize { - // The documentation says 1024 is the maximum for getrandom, but - // 1040 for /dev/random. - 1024 - } - - fn method_str(&self) -> &'static str { - match self.method { - OsRngMethod::GetRandom => "getrandom", - OsRngMethod::RandomDevice => "/dev/random", - } - } -} - -fn getrandom(buf: &mut [u8], blocking: bool) -> libc::c_long { - extern "C" { - fn syscall(number: libc::c_long, ...) -> libc::c_long; - } - - const SYS_GETRANDOM: libc::c_long = 143; - const GRND_NONBLOCK: libc::c_uint = 0x0001; - const GRND_RANDOM: libc::c_uint = 0x0002; - - unsafe { - syscall(SYS_GETRANDOM, buf.as_mut_ptr(), buf.len(), - if blocking { 0 } else { GRND_NONBLOCK } | GRND_RANDOM) - } -} - -fn getrandom_try_fill(dest: &mut [u8], blocking: bool) -> Result<(), Error> { - let result = getrandom(dest, blocking); - if result == -1 || result == 0 { - let err = io::Error::last_os_error(); - let kind = err.kind(); - if kind == io::ErrorKind::WouldBlock { - return Err(Error::with_cause( - ErrorKind::NotReady, - "getrandom not ready", - err, - )); - } else { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "unexpected getrandom error", - err, - )); - } - } else if result != dest.len() as i64 { - return Err(Error::new(ErrorKind::Unavailable, - "unexpected getrandom error")); - } - Ok(()) -} - -fn is_getrandom_available() -> bool { - use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; - use std::sync::{Once, ONCE_INIT}; - - static CHECKER: Once = ONCE_INIT; - static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT; - - CHECKER.call_once(|| { - debug!("OsRng: testing getrandom"); - let mut buf: [u8; 0] = []; - let result = getrandom(&mut buf, false); - let available = if result == -1 { - let err = io::Error::last_os_error().raw_os_error(); - err != Some(libc::ENOSYS) - } else { - true - }; - AVAILABLE.store(available, Ordering::Relaxed); - info!("OsRng: using {}", if available { "getrandom" } else { "/dev/random" }); - }); - - AVAILABLE.load(Ordering::Relaxed) -} diff --git a/rand/rand_os/src/wasm32_bindgen.rs b/rand/rand_os/src/wasm32_bindgen.rs deleted file mode 100644 index 8e7c979..0000000 --- a/rand/rand_os/src/wasm32_bindgen.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for WASM via wasm-bindgen - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -extern "C" { - pub type Function; - #[wasm_bindgen(constructor)] - pub fn new(s: &str) -> Function; - #[wasm_bindgen(method)] - pub fn call(this: &Function, self_: &JsValue) -> JsValue; - - pub type This; - #[wasm_bindgen(method, getter, structural, js_name = self)] - pub fn self_(me: &This) -> JsValue; - #[wasm_bindgen(method, getter, structural)] - pub fn crypto(me: &This) -> JsValue; - - #[derive(Clone, Debug)] - pub type BrowserCrypto; - - // TODO: these `structural` annotations here ideally wouldn't be here to - // avoid a JS shim, but for now with feature detection they're - // unavoidable. - #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)] - pub fn get_random_values_fn(me: &BrowserCrypto) -> JsValue; - #[wasm_bindgen(method, js_name = getRandomValues, structural)] - pub fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]); - - #[wasm_bindgen(js_name = require)] - pub fn node_require(s: &str) -> NodeCrypto; - - #[derive(Clone, Debug)] - pub type NodeCrypto; - - #[wasm_bindgen(method, js_name = randomFillSync, structural)] - pub fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]); -} - -#[derive(Clone, Debug)] -pub enum OsRng { - Node(NodeCrypto), - Browser(BrowserCrypto), -} - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - // First up we need to detect if we're running in node.js or a - // browser. To do this we get ahold of the `this` object (in a bit - // of a roundabout fashion). - // - // Once we have `this` we look at its `self` property, which is - // only defined on the web (either a main window or web worker). - let this = Function::new("return this").call(&JsValue::undefined()); - assert!(this != JsValue::undefined()); - let this = This::from(this); - let is_browser = this.self_() != JsValue::undefined(); - - if !is_browser { - return Ok(OsRng::Node(node_require("crypto"))) - } - - // If `self` is defined then we're in a browser somehow (main window - // or web worker). Here we want to try to use - // `crypto.getRandomValues`, but if `crypto` isn't defined we assume - // we're in an older web browser and the OS RNG isn't available. - let crypto = this.crypto(); - if crypto.is_undefined() { - let msg = "self.crypto is undefined"; - return Err(Error::new(ErrorKind::Unavailable, msg)) - } - - // Test if `crypto.getRandomValues` is undefined as well - let crypto: BrowserCrypto = crypto.into(); - if crypto.get_random_values_fn().is_undefined() { - let msg = "crypto.getRandomValues is undefined"; - return Err(Error::new(ErrorKind::Unavailable, msg)) - } - - // Ok! `self.crypto.getRandomValues` is a defined value, so let's - // assume we can do browser crypto. - Ok(OsRng::Browser(crypto)) - } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - match *self { - OsRng::Node(ref n) => n.random_fill_sync(dest), - OsRng::Browser(ref n) => n.get_random_values(dest), - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { - match *self { - OsRng::Node(_) => usize::max_value(), - OsRng::Browser(_) => { - // see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues - // - // where it says: - // - // > A QuotaExceededError DOMException is thrown if the - // > requested length is greater than 65536 bytes. - 65536 - } - } - } - - fn method_str(&self) -> &'static str { - match *self { - OsRng::Node(_) => "crypto.randomFillSync", - OsRng::Browser(_) => "crypto.getRandomValues", - } - } -} diff --git a/rand/rand_os/src/wasm32_stdweb.rs b/rand/rand_os/src/wasm32_stdweb.rs deleted file mode 100644 index 3be0ce6..0000000 --- a/rand/rand_os/src/wasm32_stdweb.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for WASM via stdweb - -use std::mem; -use stdweb::unstable::TryInto; -use stdweb::web::error::Error as WebError; -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -#[derive(Clone, Debug)] -enum OsRngMethod { - Browser, - Node -} - -#[derive(Clone, Debug)] -pub struct OsRng(OsRngMethod); - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { - let result = js! { - try { - if ( - typeof self === "object" && - typeof self.crypto === "object" && - typeof self.crypto.getRandomValues === "function" - ) { - return { success: true, ty: 1 }; - } - - if (typeof require("crypto").randomBytes === "function") { - return { success: true, ty: 2 }; - } - - return { success: false, error: new Error("not supported") }; - } catch(err) { - return { success: false, error: err }; - } - }; - - if js!{ return @{ result.as_ref() }.success } == true { - let ty = js!{ return @{ result }.ty }; - - if ty == 1 { Ok(OsRng(OsRngMethod::Browser)) } - else if ty == 2 { Ok(OsRng(OsRngMethod::Node)) } - else { unreachable!() } - } else { - let err: WebError = js!{ return @{ result }.error }.try_into().unwrap(); - Err(Error::with_cause(ErrorKind::Unavailable, "WASM Error", err)) - } - } - - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - assert_eq!(mem::size_of::<usize>(), 4); - - let len = dest.len() as u32; - let ptr = dest.as_mut_ptr() as i32; - - let result = match self.0 { - OsRngMethod::Browser => js! { - try { - let array = new Uint8Array(@{ len }); - self.crypto.getRandomValues(array); - HEAPU8.set(array, @{ ptr }); - - return { success: true }; - } catch(err) { - return { success: false, error: err }; - } - }, - OsRngMethod::Node => js! { - try { - let bytes = require("crypto").randomBytes(@{ len }); - HEAPU8.set(new Uint8Array(bytes), @{ ptr }); - - return { success: true }; - } catch(err) { - return { success: false, error: err }; - } - } - }; - - if js!{ return @{ result.as_ref() }.success } == true { - Ok(()) - } else { - let err: WebError = js!{ return @{ result }.error }.try_into().unwrap(); - Err(Error::with_cause(ErrorKind::Unexpected, "WASM Error", err)) - } - } - - fn max_chunk_size(&self) -> usize { 65536 } - - fn method_str(&self) -> &'static str { - match self.0 { - OsRngMethod::Browser => "Crypto.getRandomValues", - OsRngMethod::Node => "crypto.randomBytes", - } - } -} diff --git a/rand/rand_os/src/windows.rs b/rand/rand_os/src/windows.rs deleted file mode 100644 index 6b06c7a..0000000 --- a/rand/rand_os/src/windows.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Windows - -extern crate winapi; - -use rand_core::{Error, ErrorKind}; -use super::OsRngImpl; - -use std::io; - -use self::winapi::shared::minwindef::ULONG; -use self::winapi::um::ntsecapi::RtlGenRandom; -use self::winapi::um::winnt::PVOID; - -#[derive(Clone, Debug)] -pub struct OsRng; - -impl OsRngImpl for OsRng { - fn new() -> Result<OsRng, Error> { Ok(OsRng) } - - fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { - RtlGenRandom(dest.as_mut_ptr() as PVOID, dest.len() as ULONG) - }; - if ret == 0 { - return Err(Error::with_cause( - ErrorKind::Unavailable, - "couldn't generate random bytes", - io::Error::last_os_error())); - } - Ok(()) - } - - fn max_chunk_size(&self) -> usize { <ULONG>::max_value() as usize } - - fn method_str(&self) -> &'static str { "RtlGenRandom" } -} diff --git a/rand/rand_os/tests/mod.rs b/rand/rand_os/tests/mod.rs deleted file mode 100644 index 2130e16..0000000 --- a/rand/rand_os/tests/mod.rs +++ /dev/null @@ -1,80 +0,0 @@ -extern crate rand_os; - -use rand_os::rand_core::RngCore; -use rand_os::OsRng; - -#[test] -fn test_os_rng() { - let mut r = OsRng::new().unwrap(); - - r.next_u32(); - r.next_u64(); - - let mut v1 = [0u8; 1000]; - r.fill_bytes(&mut v1); - - let mut v2 = [0u8; 1000]; - r.fill_bytes(&mut v2); - - let mut n_diff_bits = 0; - for i in 0..v1.len() { - n_diff_bits += (v1[i] ^ v2[i]).count_ones(); - } - - // Check at least 1 bit per byte differs. p(failure) < 1e-1000 with random input. - assert!(n_diff_bits >= v1.len() as u32); -} - -#[test] -fn test_os_rng_empty() { - let mut r = OsRng::new().unwrap(); - - let mut empty = [0u8; 0]; - r.fill_bytes(&mut empty); -} - -#[test] -fn test_os_rng_huge() { - let mut r = OsRng::new().unwrap(); - - let mut huge = [0u8; 100_000]; - r.fill_bytes(&mut huge); -} - -#[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))] -#[test] -fn test_os_rng_tasks() { - use std::sync::mpsc::channel; - use std::thread; - - let mut txs = vec!(); - for _ in 0..20 { - let (tx, rx) = channel(); - txs.push(tx); - - thread::spawn(move|| { - // wait until all the tasks are ready to go. - rx.recv().unwrap(); - - // deschedule to attempt to interleave things as much - // as possible (XXX: is this a good test?) - let mut r = OsRng::new().unwrap(); - thread::yield_now(); - let mut v = [0u8; 1000]; - - for _ in 0..100 { - r.next_u32(); - thread::yield_now(); - r.next_u64(); - thread::yield_now(); - r.fill_bytes(&mut v); - thread::yield_now(); - } - }); - } - - // start all the tasks - for tx in txs.iter() { - tx.send(()).unwrap(); - } -} diff --git a/rand/rand_pcg/CHANGELOG.md b/rand/rand_pcg/CHANGELOG.md index 6f793cf..a9b82fd 100644 --- a/rand/rand_pcg/CHANGELOG.md +++ b/rand/rand_pcg/CHANGELOG.md @@ -4,9 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.1.2] - unreleased -- potential blocker: https://github.com/TyOverby/bincode/issues/250 -- make `bincode` a dev-dependency again +## [0.2.0] - 2019-06-12 +- Add `Lcg128Xsl64` aka `Pcg64` +- Bump minor crate version since rand_core bump is a breaking change +- Switch to Edition 2018 + +## [0.1.2] - 2019-02-23 +- require `bincode` 1.1.2 for i128 auto-detection +- make `bincode` a dev-dependency again #663 - clean up tests and Serde support ## [0.1.1] - 2018-10-04 diff --git a/rand/rand_pcg/Cargo.toml b/rand/rand_pcg/Cargo.toml index c2fcb7d..e2aa157 100644 --- a/rand/rand_pcg/Cargo.toml +++ b/rand/rand_pcg/Cargo.toml @@ -1,37 +1,32 @@ [package] name = "rand_pcg" -version = "0.1.1" +version = "0.2.0" authors = ["The Rand Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" -documentation = "https://rust-random.github.io/rand/rand_pcg" +documentation = "https://rust-random.github.io/rand/rand_pcg/" homepage = "https://crates.io/crates/rand_pcg" description = """ Selected PCG random number generators """ keywords = ["random", "rng", "pcg"] categories = ["algorithms", "no-std"] -build = "build.rs" +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } [features] -serde1 = ["serde", "serde_derive"] +serde1 = ["serde"] [dependencies] -rand_core = { path = "../rand_core", version = "0.3", default-features=false } -serde = { version = "1", optional = true } -serde_derive = { version = "^1.0.38", optional = true } +rand_core = { path = "../rand_core", version = "0.5" } +serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] # This is for testing serde, unfortunately we can't specify feature-gated dev # deps yet, see: https://github.com/rust-lang/cargo/issues/1596 -# TODO: we shouldn't have to depend on i128 directly; it breaks tests on old -# compilers. `bincode` should automatically support this. -bincode = { version = "1", features = ["i128"] } - -[build-dependencies] -autocfg = "0.1" +# We require at least 1.1.2 for i128 auto-detection +bincode = { version = "1.1.2" } diff --git a/rand/rand_pcg/README.md b/rand/rand_pcg/README.md index 4599813..fe47f2d 100644 --- a/rand/rand_pcg/README.md +++ b/rand/rand_pcg/README.md @@ -6,7 +6,7 @@ [[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_pcg) [![API](https://docs.rs/rand_pcg/badge.svg)](https://docs.rs/rand_pcg) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) Implements a selection of PCG random number generators. @@ -24,23 +24,15 @@ Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_pcg) - [API documentation (docs.rs)](https://docs.rs/rand_pcg) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_pcg/CHANGELOG.md) ## Crate Features -`rand_pcg` is `no_std` compatible. It does not require any functionality -outside of the `core` lib, thus there are no features to configure. +`rand_pcg` is `no_std` compatible by default. The `serde1` feature includes implementations of `Serialize` and `Deserialize` -for the included RNGs. NOTE: to use binary serialisation with any of the 64-bit -output (128-bit internal) RNGs, you must add the following dependency, since the -`i128` feature is not current enabled by default (this should be fixed soon): - -``` -bincode = { version = "1", features = ["i128"] } -``` - +for the included RNGs. ## License diff --git a/rand/rand_pcg/build.rs b/rand/rand_pcg/build.rs deleted file mode 100644 index 06e12a4..0000000 --- a/rand/rand_pcg/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern crate autocfg; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - let ac = autocfg::new(); - ac.emit_rustc_version(1, 26); -} diff --git a/rand/rand_pcg/src/lib.rs b/rand/rand_pcg/src/lib.rs index 9648e85..22ba4a0 100644 --- a/rand/rand_pcg/src/lib.rs +++ b/rand/rand_pcg/src/lib.rs @@ -17,11 +17,12 @@ //! - `Pcg32` aka `Lcg64Xsh32`, officially known as `pcg32`, a general //! purpose RNG. This is a good choice on both 32-bit and 64-bit CPUs //! (for 32-bit output). -//! - `Pcg64Mcg` aka `Mcg128Xsl64`, officially known as `mcg_xsl_rr_128_64`, +//! - `Pcg64` aka `Lcg128Xsl64`, officially known as `pcg64`, a general +//! purpose RNG. This is a good choice on 64-bit CPUs. +//! - `Pcg64Mcg` aka `Mcg128Xsl64`, officially known as `pcg64_fast`, //! a general purpose RNG using 128-bit multiplications. This has poor //! performance on 32-bit CPUs but is a good choice on 64-bit CPUs for -//! both 32-bit and 64-bit output. (Note: this RNG is only available using -//! Rust 1.26 or later.) +//! both 32-bit and 64-bit output. //! //! Both of these use 16 bytes of state and 128-bit seeds, and are considered //! value-stable (i.e. any change affecting the output given a fixed seed would @@ -34,15 +35,15 @@ #![deny(missing_docs)] #![deny(missing_debug_implementations)] -#![no_std] - -pub extern crate rand_core; +#![allow(clippy::unreadable_literal)] -#[cfg(feature="serde1")] extern crate serde; -#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive; +#![no_std] mod pcg64; -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] mod pcg128; +#[cfg(not(target_os = "emscripten"))] mod pcg128; pub use self::pcg64::{Pcg32, Lcg64Xsh32}; -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] pub use self::pcg128::{Pcg64Mcg, Mcg128Xsl64}; +#[cfg(not(target_os = "emscripten"))] pub use self::pcg128::{ + Pcg64, Lcg128Xsl64, + Pcg64Mcg, Mcg128Xsl64, +}; diff --git a/rand/rand_pcg/src/pcg128.rs b/rand/rand_pcg/src/pcg128.rs index 9aff506..311a41b 100644 --- a/rand/rand_pcg/src/pcg128.rs +++ b/rand/rand_pcg/src/pcg128.rs @@ -14,8 +14,109 @@ const MULTIPLIER: u128 = 0x2360_ED05_1FC6_5DA4_4385_DF64_9FCC_F645; use core::fmt; -use core::mem::transmute; use rand_core::{RngCore, SeedableRng, Error, le}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; + +/// A PCG random number generator (XSL RR 128/64 (LCG) variant). +/// +/// Permuted Congruential Generator with 128-bit state, internal Linear +/// Congruential Generator, and 64-bit output via "xorshift low (bits), +/// random rotation" output function. +/// +/// This is a 128-bit LCG with explicitly chosen stream with the PCG-XSL-RR +/// output function. This combination is the standard `pcg64`. +/// +/// Despite the name, this implementation uses 32 bytes (256 bit) space +/// comprising 128 bits of state and 128 bits stream selector. These are both +/// set by `SeedableRng`, using a 256-bit seed. +#[derive(Clone)] +#[cfg_attr(feature="serde1", derive(Serialize,Deserialize))] +pub struct Lcg128Xsl64 { + state: u128, + increment: u128, +} + +/// `Lcg128Xsl64` is also officially known as `pcg64`. +pub type Pcg64 = Lcg128Xsl64; + +impl Lcg128Xsl64 { + /// Construct an instance compatible with PCG seed and stream. + /// + /// Note that PCG specifies default values for both parameters: + /// + /// - `state = 0xcafef00dd15ea5e5` + /// - `stream = 0xa02bdbf7bb3c0a7ac28fa16a64abf96` + pub fn new(state: u128, stream: u128) -> Self { + // The increment must be odd, hence we discard one bit: + let increment = (stream << 1) | 1; + Lcg128Xsl64::from_state_incr(state, increment) + } + + #[inline] + fn from_state_incr(state: u128, increment: u128) -> Self { + let mut pcg = Lcg128Xsl64 { state, increment }; + // Move away from inital value: + pcg.state = pcg.state.wrapping_add(pcg.increment); + pcg.step(); + pcg + } + + #[inline] + fn step(&mut self) { + // prepare the LCG for the next round + self.state = self.state + .wrapping_mul(MULTIPLIER) + .wrapping_add(self.increment); + } +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for Lcg128Xsl64 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Lcg128Xsl64 {{}}") + } +} + +/// We use a single 255-bit seed to initialise the state and select a stream. +/// One `seed` bit (lowest bit of `seed[8]`) is ignored. +impl SeedableRng for Lcg128Xsl64 { + type Seed = [u8; 32]; + + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u64 = [0u64; 4]; + le::read_u64_into(&seed, &mut seed_u64); + let state = u128::from(seed_u64[0]) | (u128::from(seed_u64[1]) << 64); + let incr = u128::from(seed_u64[2]) | (u128::from(seed_u64[3]) << 64); + + // The increment must be odd, hence we discard one bit: + Lcg128Xsl64::from_state_incr(state, incr | 1) + } +} + +impl RngCore for Lcg128Xsl64 { + #[inline] + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.step(); + output_xsl_rr(self.state) + } + + #[inline] + fn fill_bytes(&mut self, dest: &mut [u8]) { + fill_bytes_impl(self, dest) + } + + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.fill_bytes(dest); + Ok(()) + } +} + /// A PCG random number generator (XSL 128/64 (MCG) variant). /// @@ -23,19 +124,18 @@ use rand_core::{RngCore, SeedableRng, Error, le}; /// Congruential Generator, and 64-bit output via "xorshift low (bits), /// random rotation" output function. /// -/// This is a 128-bit MCG with the PCG-XSL-RR output function. +/// This is a 128-bit MCG with the PCG-XSL-RR output function, also known as +/// `pcg64_fast`. /// Note that compared to the standard `pcg64` (128-bit LCG with PCG-XSL-RR /// output function), this RNG is faster, also has a long cycle, and still has /// good performance on statistical tests. -/// -/// Note: this RNG is only available using Rust 1.26 or later. #[derive(Clone)] #[cfg_attr(feature="serde1", derive(Serialize,Deserialize))] pub struct Mcg128Xsl64 { state: u128, } -/// A friendly name for `Mcg128Xsl64`. +/// A friendly name for `Mcg128Xsl64` (also known as `pcg64_fast`). pub type Pcg64Mcg = Mcg128Xsl64; impl Mcg128Xsl64 { @@ -66,8 +166,8 @@ impl SeedableRng for Mcg128Xsl64 { // Read as if a little-endian u128 value: let mut seed_u64 = [0u64; 2]; le::read_u64_into(&seed, &mut seed_u64); - let state = (seed_u64[0] as u128) | - (seed_u64[1] as u128) << 64; + let state = u128::from(seed_u64[0]) | + u128::from(seed_u64[1]) << 64; Mcg128Xsl64::new(state) } } @@ -80,43 +180,46 @@ impl RngCore for Mcg128Xsl64 { #[inline] fn next_u64(&mut self) -> u64 { - // prepare the LCG for the next round - let state = self.state.wrapping_mul(MULTIPLIER); - self.state = state; - - // Output function XSL RR ("xorshift low (bits), random rotation") - // Constants are for 128-bit state, 64-bit output - const XSHIFT: u32 = 64; // (128 - 64 + 64) / 2 - const ROTATE: u32 = 122; // 128 - 6 - - let rot = (state >> ROTATE) as u32; - let xsl = ((state >> XSHIFT) as u64) ^ (state as u64); - xsl.rotate_right(rot) + self.state = self.state.wrapping_mul(MULTIPLIER); + output_xsl_rr(self.state) } #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { - // specialisation of impls::fill_bytes_via_next; approx 3x faster - let mut left = dest; - while left.len() >= 8 { - let (l, r) = {left}.split_at_mut(8); - left = r; - let chunk: [u8; 8] = unsafe { - transmute(self.next_u64().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 8] = unsafe { - transmute(self.next_u64().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } + fill_bytes_impl(self, dest) } #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + self.fill_bytes(dest); + Ok(()) + } +} + +#[inline(always)] +fn output_xsl_rr(state: u128) -> u64 { + // Output function XSL RR ("xorshift low (bits), random rotation") + // Constants are for 128-bit state, 64-bit output + const XSHIFT: u32 = 64; // (128 - 64 + 64) / 2 + const ROTATE: u32 = 122; // 128 - 6 + + let rot = (state >> ROTATE) as u32; + let xsl = ((state >> XSHIFT) as u64) ^ (state as u64); + xsl.rotate_right(rot) +} + +#[inline(always)] +fn fill_bytes_impl<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) { + let mut left = dest; + while left.len() >= 8 { + let (l, r) = {left}.split_at_mut(8); + left = r; + let chunk: [u8; 8] = rng.next_u64().to_le_bytes(); + l.copy_from_slice(&chunk); + } + let n = left.len(); + if n > 0 { + let chunk: [u8; 8] = rng.next_u64().to_le_bytes(); + left.copy_from_slice(&chunk[..n]); } } diff --git a/rand/rand_pcg/src/pcg64.rs b/rand/rand_pcg/src/pcg64.rs index 9177ec2..fadc6dc 100644 --- a/rand/rand_pcg/src/pcg64.rs +++ b/rand/rand_pcg/src/pcg64.rs @@ -11,8 +11,8 @@ //! PCG random number generators use core::fmt; -use core::mem::transmute; use rand_core::{RngCore, SeedableRng, Error, le, impls}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; // This is the default multiplier used by PCG for 64-bit state. const MULTIPLIER: u64 = 6364136223846793005; @@ -45,7 +45,8 @@ impl Lcg64Xsh32 { /// Note that PCG specifies default values for both parameters: /// /// - `state = 0xcafef00dd15ea5e5` - /// - `stream = 721347520444481703` + /// - `stream = 0xa02bdbf7bb3c0a7` + // Note: stream is 1442695040888963407u64 >> 1 pub fn new(state: u64, stream: u64) -> Self { // The increment must be odd, hence we discard one bit: let increment = (stream << 1) | 1; @@ -115,27 +116,12 @@ impl RngCore for Lcg64Xsh32 { #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { - // specialisation of impls::fill_bytes_via_next; approx 40% faster - let mut left = dest; - while left.len() >= 4 { - let (l, r) = {left}.split_at_mut(4); - left = r; - let chunk: [u8; 4] = unsafe { - transmute(self.next_u32().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 4] = unsafe { - transmute(self.next_u32().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } + impls::fill_bytes_via_next(self, dest) } #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + self.fill_bytes(dest); + Ok(()) } } diff --git a/rand/rand_pcg/tests/lcg128xsl64.rs b/rand/rand_pcg/tests/lcg128xsl64.rs new file mode 100644 index 0000000..efc72ff --- /dev/null +++ b/rand/rand_pcg/tests/lcg128xsl64.rs @@ -0,0 +1,55 @@ +use rand_core::{RngCore, SeedableRng}; +use rand_pcg::{Lcg128Xsl64, Pcg64}; + +#[test] +fn test_lcg128xsl64_construction() { + // Test that various construction techniques produce a working RNG. + let seed = [1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16, + 17,18,19,20, 21,22,23,24, 25,26,27,28, 29,30,31,32]; + let mut rng1 = Lcg128Xsl64::from_seed(seed); + assert_eq!(rng1.next_u64(), 8740028313290271629); + + let mut rng2 = Lcg128Xsl64::from_rng(&mut rng1).unwrap(); + assert_eq!(rng2.next_u64(), 1922280315005786345); + + let mut rng3 = Lcg128Xsl64::seed_from_u64(0); + assert_eq!(rng3.next_u64(), 2354861276966075475); + + // This is the same as Lcg128Xsl64, so we only have a single test: + let mut rng4 = Pcg64::seed_from_u64(0); + assert_eq!(rng4.next_u64(), 2354861276966075475); +} + +#[test] +fn test_lcg128xsl64_true_values() { + // Numbers copied from official test suite (C version). + let mut rng = Lcg128Xsl64::new(42, 54); + + let mut results = [0u64; 6]; + for i in results.iter_mut() { *i = rng.next_u64(); } + let expected: [u64; 6] = [0x86b1da1d72062b68, 0x1304aa46c9853d39, + 0xa3670e9e0dd50358, 0xf9090e529a7dae00, 0xc85b9fd837996f2c, 0x606121f8e3919196]; + assert_eq!(results, expected); +} + +#[cfg(feature="serde1")] +#[test] +fn test_lcg128xsl64_serde() { + use bincode; + use std::io::{BufWriter, BufReader}; + + let mut rng = Lcg128Xsl64::seed_from_u64(0); + + let buf: Vec<u8> = Vec::new(); + let mut buf = BufWriter::new(buf); + bincode::serialize_into(&mut buf, &rng).expect("Could not serialize"); + + let buf = buf.into_inner().unwrap(); + let mut read = BufReader::new(&buf[..]); + let mut deserialized: Lcg128Xsl64 = bincode::deserialize_from(&mut read) + .expect("Could not deserialize"); + + for _ in 0..16 { + assert_eq!(rng.next_u64(), deserialized.next_u64()); + } +} diff --git a/rand/rand_pcg/tests/lcg64xsh32.rs b/rand/rand_pcg/tests/lcg64xsh32.rs index 775b12c..e05bcc1 100644 --- a/rand/rand_pcg/tests/lcg64xsh32.rs +++ b/rand/rand_pcg/tests/lcg64xsh32.rs @@ -1,7 +1,3 @@ -extern crate rand_pcg; -extern crate rand_core; -#[cfg(all(feature="serde1", test))] extern crate bincode; - use rand_core::{RngCore, SeedableRng}; use rand_pcg::{Lcg64Xsh32, Pcg32}; diff --git a/rand/rand_pcg/tests/mcg128xsl64.rs b/rand/rand_pcg/tests/mcg128xsl64.rs index 3279536..d58fa75 100644 --- a/rand/rand_pcg/tests/mcg128xsl64.rs +++ b/rand/rand_pcg/tests/mcg128xsl64.rs @@ -1,8 +1,3 @@ -#![cfg(rustc_1_26)] -extern crate rand_pcg; -extern crate rand_core; -#[cfg(all(feature="serde1", test))] extern crate bincode; - use rand_core::{RngCore, SeedableRng}; use rand_pcg::{Mcg128Xsl64, Pcg64Mcg}; diff --git a/rand/rand_xorshift/CHANGELOG.md b/rand/rand_xorshift/CHANGELOG.md index 539af41..ce3098a 100644 --- a/rand/rand_xorshift/CHANGELOG.md +++ b/rand/rand_xorshift/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2019-06-12 +- Bump minor crate version since rand_core bump is a breaking change +- Switch to Edition 2018 + +## [0.1.2] - 2019-06-06 - yanked +- Bump `rand_core` version +- Make XorShiftRng::from_rng portable by enforcing Endianness (#815) + ## [0.1.1] - 2019-01-04 - Reorganise code and tests; tweak doc diff --git a/rand/rand_xorshift/Cargo.toml b/rand/rand_xorshift/Cargo.toml index 114fee9..9cb257b 100644 --- a/rand/rand_xorshift/Cargo.toml +++ b/rand/rand_xorshift/Cargo.toml @@ -1,29 +1,29 @@ [package] name = "rand_xorshift" -version = "0.1.1" +version = "0.2.0" authors = ["The Rand Project Developers", "The Rust Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" -documentation = "https://rust-random.github.io/rand/rand_xorshift" +documentation = "https://rust-random.github.io/rand/rand_xorshift/" homepage = "https://crates.io/crates/rand_xorshift" description = """ Xorshift random number generator """ keywords = ["random", "rng", "xorshift"] categories = ["algorithms", "no-std"] +edition = "2018" [badges] travis-ci = { repository = "rust-random/rand" } appveyor = { repository = "rust-random/rand" } [features] -serde1 = ["serde", "serde_derive"] +serde1 = ["serde"] [dependencies] -rand_core = { path = "../rand_core", version = ">=0.2, <0.4", default-features=false } -serde = { version = "1", optional = true } -serde_derive = { version = "^1.0.38", optional = true } +rand_core = { path = "../rand_core", version = "0.5" } +serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] # This is for testing serde, unfortunately we can't specify feature-gated dev diff --git a/rand/rand_xorshift/README.md b/rand/rand_xorshift/README.md index 573ee12..57de284 100644 --- a/rand/rand_xorshift/README.md +++ b/rand/rand_xorshift/README.md @@ -6,7 +6,7 @@ [![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_xorshift) [![API](https://docs.rs/rand_xorshift/badge.svg)](https://docs.rs/rand_xorshift) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) Implements the Xorshift random number generator. @@ -22,7 +22,7 @@ Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_xorshift) - [API documentation (docs.rs)](https://docs.rs/rand_xorshift) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_xorshift/CHANGELOG.md) [rand]: https://crates.io/crates/rand diff --git a/rand/rand_xorshift/src/lib.rs b/rand/rand_xorshift/src/lib.rs index db42ba2..b9fef23 100644 --- a/rand/rand_xorshift/src/lib.rs +++ b/rand/rand_xorshift/src/lib.rs @@ -17,14 +17,10 @@ #![no_std] -pub extern crate rand_core; - -#[cfg(feature="serde1")] extern crate serde; -#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive; - use core::num::Wrapping as w; -use core::{fmt, slice}; +use core::fmt; use rand_core::{RngCore, SeedableRng, Error, impls, le}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; /// An Xorshift random number generator. /// @@ -75,7 +71,8 @@ impl RngCore for XorShiftRng { } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + self.fill_bytes(dest); + Ok(()) } } @@ -102,22 +99,19 @@ impl SeedableRng for XorShiftRng { } fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, Error> { - let mut seed_u32 = [0u32; 4]; + let mut b = [0u8; 16]; loop { - unsafe { - let ptr = seed_u32.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, 4 * 4); - rng.try_fill_bytes(slice)?; + rng.try_fill_bytes(&mut b[..])?; + if !b.iter().all(|&x| x == 0) { + break; } - if !seed_u32.iter().all(|&x| x == 0) { break; } } Ok(XorShiftRng { - x: w(seed_u32[0]), - y: w(seed_u32[1]), - z: w(seed_u32[2]), - w: w(seed_u32[3]), + x: w(u32::from_le_bytes([b[0], b[1], b[2], b[3]])), + y: w(u32::from_le_bytes([b[4], b[5], b[6], b[7]])), + z: w(u32::from_le_bytes([b[8], b[9], b[10], b[11]])), + w: w(u32::from_le_bytes([b[12], b[13], b[14], b[15]])), }) } } diff --git a/rand/rand_xorshift/tests/mod.rs b/rand/rand_xorshift/tests/mod.rs index 8374b64..7ecdeae 100644 --- a/rand/rand_xorshift/tests/mod.rs +++ b/rand/rand_xorshift/tests/mod.rs @@ -1,7 +1,3 @@ -extern crate rand_core; -extern crate rand_xorshift; -#[cfg(all(feature="serde1", test))] extern crate bincode; - use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -12,9 +8,10 @@ fn test_xorshift_construction() { let mut rng1 = XorShiftRng::from_seed(seed); assert_eq!(rng1.next_u64(), 4325440999699518727); - let _rng2 = XorShiftRng::from_rng(rng1).unwrap(); - // Note: we cannot test the state of _rng2 because from_rng does not - // fix Endianness. This is allowed in the trait specification. + let mut rng2 = XorShiftRng::from_rng(&mut rng1).unwrap(); + // Yes, this makes rng2 a clone of rng1! + assert_eq!(rng1.next_u64(), 15614385950550801700); + assert_eq!(rng2.next_u64(), 15614385950550801700); } #[test] diff --git a/rand/rand_xoshiro/CHANGELOG.md b/rand/rand_xoshiro/CHANGELOG.md index b23c990..56cb9c2 100644 --- a/rand/rand_xoshiro/CHANGELOG.md +++ b/rand/rand_xoshiro/CHANGELOG.md @@ -4,5 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.1] - 2019-08-06 +- Drop `byteorder`-dependency in favor of `stdlib`-implementation. + +## [0.3.0] - 2019-06-12 +- Bump minor crate version since rand_core bump is a breaking change +- Switch to Edition 2018 + +## [0.2.1] - 2019-06-06 - yanked +- Bump `rand_core` version +- Document crate features in README + +## [0.2.0] - 2019-05-28 +- Fix `seed_from_u64(0)` for `Xoroshiro64StarStar` and `Xoroshiro64Star`. This + breaks value stability for these generators if initialized with `seed_from_u64`. +- Implement Serde support. + ## [0.1.0] - 2019-01-04 Initial release. diff --git a/rand/rand_xoshiro/Cargo.toml b/rand/rand_xoshiro/Cargo.toml index 8d174c7..128c213 100644 --- a/rand/rand_xoshiro/Cargo.toml +++ b/rand/rand_xoshiro/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "rand_xoshiro" -version = "0.1.0" # NB: When modifying, also modify html_root_url in lib.rs +version = "0.3.1" # NB: When modifying, also modify html_root_url in lib.rs authors = ["The Rand Project Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/rust-random/rand" documentation = "https://docs.rs/rand_xoshiro" @@ -10,10 +10,16 @@ homepage = "https://crates.io/crates/rand_xoshiro" description = "Xoshiro, xoroshiro and splitmix64 random number generators" keywords = ["random", "rng"] categories = ["algorithms"] +edition = "2018" + +[features] +serde1 = ["serde"] [dependencies] -byteorder = { version = "1", default-features=false } -rand_core = { path = "../rand_core", version = "0.3", default-features=false } +rand_core = { path = "../rand_core", version = "0.5" } +serde = { version = "1", features = ["derive"], optional=true } [dev-dependencies] -rand = { path = "..", version = "0.6", default-features=false } # needed for doctests +# This is for testing serde, unfortunately we can't specify feature-gated dev +# deps yet, see: https://github.com/rust-lang/cargo/issues/1596 +bincode = { version = "1" } diff --git a/rand/rand_xoshiro/README.md b/rand/rand_xoshiro/README.md index 014477e..1c02992 100644 --- a/rand/rand_xoshiro/README.md +++ b/rand/rand_xoshiro/README.md @@ -6,7 +6,7 @@ [![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) [![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_xoshiro) [![API](https://docs.rs/rand_xoshiro/badge.svg)](https://docs.rs/rand_xoshiro) -[![Minimum rustc version](https://img.shields.io/badge/rustc-1.22+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) +[![Minimum rustc version](https://img.shields.io/badge/rustc-1.32+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) Rust implementation of the [xoshiro, xoroshiro and splitmix64](http://xoshiro.di.unimi.it) random number generators. @@ -17,7 +17,13 @@ Links: - [API documentation (master)](https://rust-random.github.io/rand/rand_xoshiro) - [API documentation (docs.rs)](https://docs.rs/rand_xoshiro) -- [Changelog](CHANGELOG.md) +- [Changelog](https://github.com/rust-random/rand/blob/master/rand_xoshiro/CHANGELOG.md) + +## Crate Features + +`rand_xoshiro` is no_std compatible by default. + +The `serde1` feature includes implementations of `Serialize` and `Deserialize` for the included RNGs. ## License diff --git a/rand/rand_xoshiro/src/common.rs b/rand/rand_xoshiro/src/common.rs index 9ee09e2..b188dd6 100644 --- a/rand/rand_xoshiro/src/common.rs +++ b/rand/rand_xoshiro/src/common.rs @@ -9,7 +9,7 @@ /// Initialize a RNG from a `u64` seed using `SplitMix64`. macro_rules! from_splitmix { ($seed:expr) => { { - let mut rng = ::SplitMix64::seed_from_u64($seed); + let mut rng = crate::SplitMix64::seed_from_u64($seed); Self::from_rng(&mut rng).unwrap() } } } diff --git a/rand/rand_xoshiro/src/lib.rs b/rand/rand_xoshiro/src/lib.rs index 634db31..3047e92 100644 --- a/rand/rand_xoshiro/src/lib.rs +++ b/rand/rand_xoshiro/src/lib.rs @@ -55,28 +55,15 @@ //! //! [xoshiro]: http://xoshiro.di.unimi.it/ //! [low linear complexity]: http://xoshiro.di.unimi.it/lowcomp.php -//! [`Xoshiro256StarStar`]: ./struct.Xoshiro256StarStar.html -//! [`Xoshiro256Plus`]: ./struct.Xoshiro256Plus.html -//! [`Xoroshiro128StarStar`]: ./struct.Xoroshiro128StarStar.html -//! [`Xoroshiro128Plus`]: ./struct.Xoroshiro128Plus.html -//! [`Xoshiro512StarStar`]: ./struct.Xoshiro512StarStar.html -//! [`Xoshiro512Plus`]: ./struct.Xoshiro512Plus.html -//! [`SplitMix64`]: ./struct.SplitMix64.html -//! [`Xoshiro128StarStar`]: ./struct.Xoshiro128StarStar.html -//! [`Xoshiro128Plus`]: ./struct.Xoshiro128Plus.html -//! [`Xoroshiro64StarStar`]: ./struct.Xoroshiro64StarStar.html -//! [`Xoroshiro64Star`]: ./struct.Xoroshiro64Star.html #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", - html_root_url = "https://docs.rs/rand_xoshiro/0.1.0")] + html_root_url = "https://docs.rs/rand_xoshiro/0.3.1")] #![deny(missing_docs)] #![deny(missing_debug_implementations)] -#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] +#![allow(clippy::unreadable_literal)] #![no_std] -extern crate byteorder; -pub extern crate rand_core; #[macro_use] mod common; @@ -92,6 +79,7 @@ mod xoroshiro128starstar; mod xoroshiro64starstar; mod xoroshiro64star; +pub use rand_core; pub use splitmix64::SplitMix64; pub use xoshiro128starstar::Xoshiro128StarStar; pub use xoshiro128plus::Xoshiro128Plus; diff --git a/rand/rand_xoshiro/src/splitmix64.rs b/rand/rand_xoshiro/src/splitmix64.rs index a7cac9f..3a41450 100644 --- a/rand/rand_xoshiro/src/splitmix64.rs +++ b/rand/rand_xoshiro/src/splitmix64.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use byteorder::{ByteOrder, LittleEndian}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::le::read_u64_into; use rand_core::impls::fill_bytes_via_next; use rand_core::{RngCore, SeedableRng, Error}; @@ -22,6 +22,7 @@ use rand_core::{RngCore, SeedableRng, Error}; /// from [`dsiutils`](http://dsiutils.di.unimi.it/) is used. #[allow(missing_copy_implementations)] #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct SplitMix64 { x: u64, } @@ -77,9 +78,7 @@ impl SeedableRng for SplitMix64 { /// Seed a `SplitMix64` from a `u64`. fn seed_from_u64(seed: u64) -> SplitMix64 { - let mut x = [0; 8]; - LittleEndian::write_u64(&mut x, seed); - SplitMix64::from_seed(x) + SplitMix64::from_seed(seed.to_le_bytes()) } } diff --git a/rand/rand_xoshiro/src/xoroshiro128plus.rs b/rand/rand_xoshiro/src/xoroshiro128plus.rs index df032c8..a7b4ebc 100644 --- a/rand/rand_xoshiro/src/xoroshiro128plus.rs +++ b/rand/rand_xoshiro/src/xoroshiro128plus.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core; use rand_core::le::read_u64_into; use rand_core::impls::fill_bytes_via_next; @@ -22,6 +23,7 @@ use rand_core::{RngCore, SeedableRng}; /// David Blackman and Sebastiano Vigna. #[allow(missing_copy_implementations)] #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoroshiro128Plus { s0: u64, s1: u64, @@ -34,10 +36,7 @@ impl Xoroshiro128Plus { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoroshiro128Plus; /// /// let rng1 = Xoroshiro128Plus::seed_from_u64(0); @@ -45,7 +44,6 @@ impl Xoroshiro128Plus { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u64, self, [0xdf900294d8f554a5, 0x170865df4b3201fc]); diff --git a/rand/rand_xoshiro/src/xoroshiro128starstar.rs b/rand/rand_xoshiro/src/xoroshiro128starstar.rs index 2d27850..21823f5 100644 --- a/rand/rand_xoshiro/src/xoroshiro128starstar.rs +++ b/rand/rand_xoshiro/src/xoroshiro128starstar.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core; use rand_core::le::read_u64_into; use rand_core::impls::fill_bytes_via_next; @@ -21,6 +22,7 @@ use rand_core::{RngCore, SeedableRng}; /// David Blackman and Sebastiano Vigna. #[allow(missing_copy_implementations)] #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoroshiro128StarStar { s0: u64, s1: u64, @@ -33,10 +35,7 @@ impl Xoroshiro128StarStar { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoroshiro128StarStar; /// /// let rng1 = Xoroshiro128StarStar::seed_from_u64(0); @@ -44,7 +43,6 @@ impl Xoroshiro128StarStar { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u64, self, [0xdf900294d8f554a5, 0x170865df4b3201fc]); diff --git a/rand/rand_xoshiro/src/xoroshiro64star.rs b/rand/rand_xoshiro/src/xoroshiro64star.rs index 86338fd..6bb708a 100644 --- a/rand/rand_xoshiro/src/xoroshiro64star.rs +++ b/rand/rand_xoshiro/src/xoroshiro64star.rs @@ -6,7 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use byteorder::{ByteOrder, LittleEndian}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core; use rand_core::le::read_u32_into; use rand_core::impls::{fill_bytes_via_next, next_u64_via_u32}; @@ -23,6 +23,7 @@ use rand_core::{RngCore, SeedableRng}; /// David Blackman and Sebastiano Vigna. #[allow(missing_copy_implementations)] #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoroshiro64Star { s0: u32, s1: u32, @@ -71,9 +72,7 @@ impl SeedableRng for Xoroshiro64Star { /// Seed a `Xoroshiro64Star` from a `u64` using `SplitMix64`. fn seed_from_u64(seed: u64) -> Xoroshiro64Star { - let mut s = [0; 8]; - LittleEndian::write_u64(&mut s, seed); - Xoroshiro64Star::from_seed(s) + from_splitmix!(seed) } } @@ -94,4 +93,10 @@ mod tests { assert_eq!(rng.next_u32(), e); } } + + #[test] + fn zero_seed() { + let mut rng = Xoroshiro64Star::seed_from_u64(0); + assert_ne!(rng.next_u64(), 0); + } } diff --git a/rand/rand_xoshiro/src/xoroshiro64starstar.rs b/rand/rand_xoshiro/src/xoroshiro64starstar.rs index a40baee..8e1aea1 100644 --- a/rand/rand_xoshiro/src/xoroshiro64starstar.rs +++ b/rand/rand_xoshiro/src/xoroshiro64starstar.rs @@ -6,13 +6,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use byteorder::{ByteOrder, LittleEndian}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core; use rand_core::le::read_u32_into; use rand_core::impls::{fill_bytes_via_next, next_u64_via_u32}; use rand_core::{RngCore, SeedableRng}; -/// A Xoroshiro64** random number generator. +/// A xoroshiro64** random number generator. /// /// The xoshiro64** algorithm is not suitable for cryptographic purposes, but /// is very fast and has excellent statistical properties. @@ -22,6 +22,7 @@ use rand_core::{RngCore, SeedableRng}; /// David Blackman and Sebastiano Vigna. #[allow(missing_copy_implementations)] #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoroshiro64StarStar { s0: u32, s1: u32, @@ -68,11 +69,9 @@ impl SeedableRng for Xoroshiro64StarStar { } } - /// Seed a `Xoroshiro64StarStar` from a `u64`. + /// Seed a `Xoroshiro64StarStar` from a `u64` using `SplitMix64`. fn seed_from_u64(seed: u64) -> Xoroshiro64StarStar { - let mut s = [0; 8]; - LittleEndian::write_u64(&mut s, seed); - Xoroshiro64StarStar::from_seed(s) + from_splitmix!(seed) } } @@ -93,4 +92,10 @@ mod tests { assert_eq!(rng.next_u32(), e); } } + + #[test] + fn zero_seed() { + let mut rng = Xoroshiro64StarStar::seed_from_u64(0); + assert_ne!(rng.next_u64(), 0); + } } diff --git a/rand/rand_xoshiro/src/xoshiro128plus.rs b/rand/rand_xoshiro/src/xoshiro128plus.rs index b0c7cc7..7cbd612 100644 --- a/rand/rand_xoshiro/src/xoshiro128plus.rs +++ b/rand/rand_xoshiro/src/xoshiro128plus.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::impls::{next_u64_via_u32, fill_bytes_via_next}; use rand_core::le::read_u32_into; use rand_core::{SeedableRng, RngCore, Error}; @@ -20,6 +21,7 @@ use rand_core::{SeedableRng, RngCore, Error}; /// reference source code](http://xoshiro.di.unimi.it/xoshiro128starstar.c) by /// David Blackman and Sebastiano Vigna. #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoshiro128Plus { s: [u32; 4], } @@ -31,10 +33,7 @@ impl Xoshiro128Plus { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoroshiro128StarStar; /// /// let rng1 = Xoroshiro128StarStar::seed_from_u64(0); @@ -42,7 +41,6 @@ impl Xoshiro128Plus { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u32, self, [0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b]); diff --git a/rand/rand_xoshiro/src/xoshiro128starstar.rs b/rand/rand_xoshiro/src/xoshiro128starstar.rs index 836864e..7af1e50 100644 --- a/rand/rand_xoshiro/src/xoshiro128starstar.rs +++ b/rand/rand_xoshiro/src/xoshiro128starstar.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::impls::{next_u64_via_u32, fill_bytes_via_next}; use rand_core::le::read_u32_into; use rand_core::{SeedableRng, RngCore, Error}; @@ -19,6 +20,7 @@ use rand_core::{SeedableRng, RngCore, Error}; /// reference source code](http://xoshiro.di.unimi.it/xoshiro128starstar.c) by /// David Blackman and Sebastiano Vigna. #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoshiro128StarStar { s: [u32; 4], } @@ -30,10 +32,7 @@ impl Xoshiro128StarStar { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoroshiro128StarStar; /// /// let rng1 = Xoroshiro128StarStar::seed_from_u64(0); @@ -41,7 +40,6 @@ impl Xoshiro128StarStar { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u32, self, [0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b]); diff --git a/rand/rand_xoshiro/src/xoshiro256plus.rs b/rand/rand_xoshiro/src/xoshiro256plus.rs index 08da5a8..396f588 100644 --- a/rand/rand_xoshiro/src/xoshiro256plus.rs +++ b/rand/rand_xoshiro/src/xoshiro256plus.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::impls::fill_bytes_via_next; use rand_core::le::read_u64_into; use rand_core::{SeedableRng, RngCore, Error}; @@ -20,6 +21,7 @@ use rand_core::{SeedableRng, RngCore, Error}; /// reference source code](http://xoshiro.di.unimi.it/xoshiro256plus.c) by /// David Blackman and Sebastiano Vigna. #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoshiro256Plus { s: [u64; 4], } @@ -31,10 +33,7 @@ impl Xoshiro256Plus { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoshiro256Plus; /// /// let rng1 = Xoshiro256Plus::seed_from_u64(0); @@ -42,7 +41,6 @@ impl Xoshiro256Plus { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u64, self, [ diff --git a/rand/rand_xoshiro/src/xoshiro256starstar.rs b/rand/rand_xoshiro/src/xoshiro256starstar.rs index fc0a208..2cc2029 100644 --- a/rand/rand_xoshiro/src/xoshiro256starstar.rs +++ b/rand/rand_xoshiro/src/xoshiro256starstar.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::impls::fill_bytes_via_next; use rand_core::le::read_u64_into; use rand_core::{SeedableRng, RngCore, Error}; @@ -19,6 +20,7 @@ use rand_core::{SeedableRng, RngCore, Error}; /// reference source code](http://xoshiro.di.unimi.it/xoshiro256starstar.c) by /// David Blackman and Sebastiano Vigna. #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoshiro256StarStar { s: [u64; 4], } @@ -30,10 +32,7 @@ impl Xoshiro256StarStar { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoshiro256StarStar; /// /// let rng1 = Xoshiro256StarStar::seed_from_u64(0); @@ -41,7 +40,6 @@ impl Xoshiro256StarStar { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u64, self, [ diff --git a/rand/rand_xoshiro/src/xoshiro512plus.rs b/rand/rand_xoshiro/src/xoshiro512plus.rs index fe982e4..4b589f2 100644 --- a/rand/rand_xoshiro/src/xoshiro512plus.rs +++ b/rand/rand_xoshiro/src/xoshiro512plus.rs @@ -6,11 +6,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::impls::fill_bytes_via_next; use rand_core::le::read_u64_into; use rand_core::{SeedableRng, RngCore, Error}; -use Seed512; +use crate::Seed512; /// A xoshiro512+ random number generator. /// @@ -22,6 +23,7 @@ use Seed512; /// reference source code](http://xoshiro.di.unimi.it/xoshiro512plus.c) by /// David Blackman and Sebastiano Vigna. #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoshiro512Plus { s: [u64; 8], } @@ -33,10 +35,7 @@ impl Xoshiro512Plus { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoshiro512Plus; /// /// let rng1 = Xoshiro512Plus::seed_from_u64(0); @@ -44,7 +43,6 @@ impl Xoshiro512Plus { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u64, self, [ diff --git a/rand/rand_xoshiro/src/xoshiro512starstar.rs b/rand/rand_xoshiro/src/xoshiro512starstar.rs index 1a33f0a..2db9ac1 100644 --- a/rand/rand_xoshiro/src/xoshiro512starstar.rs +++ b/rand/rand_xoshiro/src/xoshiro512starstar.rs @@ -6,11 +6,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; use rand_core::impls::fill_bytes_via_next; use rand_core::le::read_u64_into; use rand_core::{SeedableRng, RngCore, Error}; -use Seed512; +use crate::Seed512; /// A xoshiro512** random number generator. /// @@ -21,6 +22,7 @@ use Seed512; /// reference source code](http://xoshiro.di.unimi.it/xoshiro512starstar.c) by /// David Blackman and Sebastiano Vigna. #[derive(Debug, Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Xoshiro512StarStar { s: [u64; 8], } @@ -32,10 +34,7 @@ impl Xoshiro512StarStar { /// parallel computations. /// /// ``` - /// # extern crate rand; - /// # extern crate rand_xoshiro; - /// # fn main() { - /// use rand::SeedableRng; + /// use rand_xoshiro::rand_core::SeedableRng; /// use rand_xoshiro::Xoshiro512StarStar; /// /// let rng1 = Xoshiro512StarStar::seed_from_u64(0); @@ -43,7 +42,6 @@ impl Xoshiro512StarStar { /// rng2.jump(); /// let mut rng3 = rng2.clone(); /// rng3.jump(); - /// # } /// ``` pub fn jump(&mut self) { impl_jump!(u64, self, [ diff --git a/rand/rand_xoshiro/tests/serde.rs b/rand/rand_xoshiro/tests/serde.rs new file mode 100644 index 0000000..ee23a1d --- /dev/null +++ b/rand/rand_xoshiro/tests/serde.rs @@ -0,0 +1,83 @@ +#![cfg(feature="serde1")] + +use rand_core::{RngCore, SeedableRng}; +use rand_xoshiro::{SplitMix64, Xoroshiro64StarStar, Xoroshiro64Star, + Xoroshiro128Plus, Xoroshiro128StarStar, Xoshiro128StarStar, Xoshiro128Plus, + Xoshiro256StarStar, Xoshiro256Plus, Xoshiro512StarStar, Xoshiro512Plus}; + +macro_rules! serde_rng { + ($rng:ident) => { + use bincode; + use std::io::{BufWriter, BufReader}; + + let mut rng = $rng::seed_from_u64(0); + + let buf: Vec<u8> = Vec::new(); + let mut buf = BufWriter::new(buf); + bincode::serialize_into(&mut buf, &rng).expect("Could not serialize"); + + let buf = buf.into_inner().unwrap(); + let mut read = BufReader::new(&buf[..]); + let mut deserialized: $rng = bincode::deserialize_from(&mut read) + .expect("Could not deserialize"); + + for _ in 0..16 { + assert_eq!(rng.next_u64(), deserialized.next_u64()); + } + } +} + +#[test] +fn test_splitmix64() { + serde_rng!(SplitMix64); +} + +#[test] +fn test_xoroshiro64starstar() { + serde_rng!(Xoroshiro64StarStar); +} + +#[test] +fn test_xoroshiro64star() { + serde_rng!(Xoroshiro64Star); +} + +#[test] +fn test_xoroshiro128plus() { + serde_rng!(Xoroshiro128Plus); +} + +#[test] +fn test_xoroshiro128starstar() { + serde_rng!(Xoroshiro128StarStar); +} + +#[test] +fn test_xoshiro128starstar() { + serde_rng!(Xoshiro128StarStar); +} + +#[test] +fn test_xoshiro128plus() { + serde_rng!(Xoshiro128Plus); +} + +#[test] +fn test_xoshiro256starstar() { + serde_rng!(Xoshiro256StarStar); +} + +#[test] +fn test_xoshiro256plus() { + serde_rng!(Xoshiro256Plus); +} + +#[test] +fn test_xoshiro512starstar() { + serde_rng!(Xoshiro512StarStar); +} + +#[test] +fn test_xoshiro512plus() { + serde_rng!(Xoshiro512Plus); +} diff --git a/rand/rustfmt.toml b/rand/rustfmt.toml new file mode 100644 index 0000000..6b2aba3 --- /dev/null +++ b/rand/rustfmt.toml @@ -0,0 +1,30 @@ +# This rustfmt file is added for configuration, but in practice much of our +# code is hand-formatted, frequently with more readable results. + +# Comments: +normalize_comments = true +wrap_comments = false +format_doc_comments = true +comment_width = 90 # small excess is okay but prefer 80 + +# Arguments: +use_small_heuristics = "max" +fn_args_density = "compressed" +fn_single_line = false +overflow_delimited_expr = true +where_single_line = true + +# enum_discrim_align_threshold = 20 +# struct_field_align_threshold = 20 + +# Compatibility: +edition = "2018" # we require compatibility back to 1.32.0 + +# Misc: +blank_lines_upper_bound = 2 +reorder_impl_items = true +# report_todo = "Unnumbered" +# report_fixme = "Unnumbered" + +# Ignored files: +ignore = [] diff --git a/rand/src/deprecated.rs b/rand/src/deprecated.rs deleted file mode 100644 index 88eb09f..0000000 --- a/rand/src/deprecated.rs +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Deprecated re-exports (we can't add deprecation warnings otherwise) - -#![allow(deprecated)] - -use rngs; -use {RngCore, CryptoRng, SeedableRng, Error}; -use rand_core::block::BlockRngCore; -use rand_isaac; -use rand_chacha; -use rand_hc; - -#[cfg(feature="std")] -use std::io::Read; - -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", - note="import from rand_isaac crate instead, or use the newer Hc128Rng")] -pub struct IsaacRng(rand_isaac::IsaacRng); - -impl RngCore for IsaacRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl SeedableRng for IsaacRng { - type Seed = <rand_isaac::IsaacRng as SeedableRng>::Seed; - - fn from_seed(seed: Self::Seed) -> Self { - IsaacRng(rand_isaac::IsaacRng::from_seed(seed)) - } - - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - rand_isaac::IsaacRng::from_rng(rng).map(IsaacRng) - } -} - -impl IsaacRng { - pub fn new_from_u64(seed: u64) -> Self { - IsaacRng(rand_isaac::IsaacRng::new_from_u64(seed)) - } -} - - -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", - note="import from rand_isaac crate instead, or use newer Hc128Rng")] -pub struct Isaac64Rng(rand_isaac::Isaac64Rng); - -impl RngCore for Isaac64Rng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl SeedableRng for Isaac64Rng { - type Seed = <rand_isaac::Isaac64Rng as SeedableRng>::Seed; - - fn from_seed(seed: Self::Seed) -> Self { - Isaac64Rng(rand_isaac::Isaac64Rng::from_seed(seed)) - } - - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - rand_isaac::Isaac64Rng::from_rng(rng).map(Isaac64Rng) - } -} - -impl Isaac64Rng { - pub fn new_from_u64(seed: u64) -> Self { - Isaac64Rng(rand_isaac::Isaac64Rng::new_from_u64(seed)) - } -} - - -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", note="import from rand_chacha crate instead")] -pub struct ChaChaRng(rand_chacha::ChaChaRng); - -impl RngCore for ChaChaRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl SeedableRng for ChaChaRng { - type Seed = <rand_chacha::ChaChaRng as SeedableRng>::Seed; - - fn from_seed(seed: Self::Seed) -> Self { - ChaChaRng(rand_chacha::ChaChaRng::from_seed(seed)) - } - - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - rand_chacha::ChaChaRng::from_rng(rng).map(ChaChaRng) - } -} - -impl ChaChaRng { - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] - pub fn get_word_pos(&self) -> u128 { - self.0.get_word_pos() - } - - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] - pub fn set_word_pos(&mut self, word_offset: u128) { - self.0.set_word_pos(word_offset) - } - - pub fn set_stream(&mut self, stream: u64) { - self.0.set_stream(stream) - } -} - -impl CryptoRng for ChaChaRng {} - - -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", note="import from rand_hc crate instead")] -pub struct Hc128Rng(rand_hc::Hc128Rng); - -impl RngCore for Hc128Rng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl SeedableRng for Hc128Rng { - type Seed = <rand_hc::Hc128Rng as SeedableRng>::Seed; - - fn from_seed(seed: Self::Seed) -> Self { - Hc128Rng(rand_hc::Hc128Rng::from_seed(seed)) - } - - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - rand_hc::Hc128Rng::from_rng(rng).map(Hc128Rng) - } -} - -impl CryptoRng for Hc128Rng {} - - -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", note="import from rand_xorshift crate instead")] -pub struct XorShiftRng(::rand_xorshift::XorShiftRng); - -impl RngCore for XorShiftRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl SeedableRng for XorShiftRng { - type Seed = <::rand_xorshift::XorShiftRng as SeedableRng>::Seed; - - fn from_seed(seed: Self::Seed) -> Self { - XorShiftRng(::rand_xorshift::XorShiftRng::from_seed(seed)) - } - - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - ::rand_xorshift::XorShiftRng::from_rng(rng).map(XorShiftRng) - } -} - - -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", - note="import with rand::prelude::* or rand::rngs::StdRng instead")] -pub struct StdRng(rngs::StdRng); - -impl RngCore for StdRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl SeedableRng for StdRng { - type Seed = <rngs::StdRng as SeedableRng>::Seed; - - fn from_seed(seed: Self::Seed) -> Self { - StdRng(rngs::StdRng::from_seed(seed)) - } - - fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - rngs::StdRng::from_rng(rng).map(StdRng) - } -} - -impl CryptoRng for StdRng {} - - -#[cfg(feature="rand_os")] -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", note="import with rand::rngs::OsRng instead")] -pub struct OsRng(rngs::OsRng); - -#[cfg(feature="rand_os")] -impl RngCore for OsRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(feature="rand_os")] -impl OsRng { - pub fn new() -> Result<Self, Error> { - rngs::OsRng::new().map(OsRng) - } -} - -#[cfg(feature="rand_os")] -impl CryptoRng for OsRng {} - - -#[cfg(feature="std")] -#[derive(Debug)] -#[deprecated(since="0.6.0", note="import with rand::rngs::EntropyRng instead")] -pub struct EntropyRng(rngs::EntropyRng); - -#[cfg(feature="std")] -impl RngCore for EntropyRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(feature="std")] -impl EntropyRng { - pub fn new() -> Self { - EntropyRng(rngs::EntropyRng::new()) - } -} - -#[cfg(feature="std")] -impl Default for EntropyRng { - fn default() -> Self { - EntropyRng::new() - } -} - -#[cfg(feature="std")] -impl CryptoRng for EntropyRng {} - - -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", note="import with rand::rngs::JitterRng instead")] -pub struct JitterRng(rngs::JitterRng); - -impl RngCore for JitterRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl JitterRng { - #[cfg(all(feature="std", not(target_arch = "wasm32")))] - pub fn new() -> Result<JitterRng, rngs::TimerError> { - rngs::JitterRng::new().map(JitterRng) - } - - pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { - JitterRng(rngs::JitterRng::new_with_timer(timer)) - } - - pub fn set_rounds(&mut self, rounds: u8) { - self.0.set_rounds(rounds) - } - - pub fn test_timer(&mut self) -> Result<u8, rngs::TimerError> { - self.0.test_timer() - } - - #[cfg(feature="std")] - pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { - self.0.timer_stats(var_rounds) - } -} - -impl CryptoRng for JitterRng {} - - -#[cfg(feature="std")] -#[derive(Clone, Debug)] -#[deprecated(since="0.6.0", - note="import with rand::prelude::* or rand::rngs::ThreadRng instead")] -pub struct ThreadRng(rngs::ThreadRng); - -#[cfg(feature="std")] -impl RngCore for ThreadRng { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(feature="std")] -impl CryptoRng for ThreadRng {} - - -#[cfg(feature="std")] -#[derive(Debug)] -#[deprecated(since="0.6.0", note="import with rand::rngs::adapter::ReadRng instead")] -pub struct ReadRng<R>(rngs::adapter::ReadRng<R>); - -#[cfg(feature="std")] -impl<R: Read> RngCore for ReadRng<R> { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest); - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(feature="std")] -impl<R: Read> ReadRng<R> { - pub fn new(r: R) -> ReadRng<R> { - ReadRng(rngs::adapter::ReadRng::new(r)) - } -} - - -#[derive(Clone, Debug)] -pub struct ReseedingRng<R, Rsdr>(rngs::adapter::ReseedingRng<R, Rsdr>) -where R: BlockRngCore + SeedableRng, - Rsdr: RngCore; - -impl<R, Rsdr: RngCore> RngCore for ReseedingRng<R, Rsdr> -where R: BlockRngCore<Item = u32> + SeedableRng, - <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]> -{ - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } - - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.0.fill_bytes(dest) - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -impl<R, Rsdr> ReseedingRng<R, Rsdr> -where R: BlockRngCore + SeedableRng, - Rsdr: RngCore -{ - pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self { - ReseedingRng(rngs::adapter::ReseedingRng::new(rng, threshold, reseeder)) - } - - pub fn reseed(&mut self) -> Result<(), Error> { - self.0.reseed() - } -} - -impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr> -where R: BlockRngCore + SeedableRng + CryptoRng, - Rsdr: RngCore + CryptoRng {} diff --git a/rand/src/distributions/bernoulli.rs b/rand/src/distributions/bernoulli.rs index f49618c..eadd056 100644 --- a/rand/src/distributions/bernoulli.rs +++ b/rand/src/distributions/bernoulli.rs @@ -8,8 +8,8 @@ //! The Bernoulli distribution. -use Rng; -use distributions::Distribution; +use crate::Rng; +use crate::distributions::Distribution; /// The Bernoulli distribution. /// @@ -20,7 +20,7 @@ use distributions::Distribution; /// ```rust /// use rand::distributions::{Bernoulli, Distribution}; /// -/// let d = Bernoulli::new(0.3); +/// let d = Bernoulli::new(0.3).unwrap(); /// let v = d.sample(&mut rand::thread_rng()); /// println!("{} is from a Bernoulli distribution", v); /// ``` @@ -61,13 +61,16 @@ const ALWAYS_TRUE: u64 = ::core::u64::MAX; // in `no_std` mode. const SCALE: f64 = 2.0 * (1u64 << 63) as f64; +/// Error type returned from `Bernoulli::new`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BernoulliError { + /// `p < 0` or `p > 1`. + InvalidProbability, +} + impl Bernoulli { /// Construct a new `Bernoulli` with the given probability of success `p`. /// - /// # Panics - /// - /// If `p < 0` or `p > 1`. - /// /// # Precision /// /// For `p = 1.0`, the resulting distribution will always generate true. @@ -77,12 +80,12 @@ impl Bernoulli { /// a multiple of 2<sup>-64</sup>. (Note that not all multiples of /// 2<sup>-64</sup> in `[0, 1]` can be represented as a `f64`.) #[inline] - pub fn new(p: f64) -> Bernoulli { + pub fn new(p: f64) -> Result<Bernoulli, BernoulliError> { if p < 0.0 || p >= 1.0 { - if p == 1.0 { return Bernoulli { p_int: ALWAYS_TRUE } } - panic!("Bernoulli::new not called with 0.0 <= p <= 1.0"); + if p == 1.0 { return Ok(Bernoulli { p_int: ALWAYS_TRUE }) } + return Err(BernoulliError::InvalidProbability); } - Bernoulli { p_int: (p * SCALE) as u64 } + Ok(Bernoulli { p_int: (p * SCALE) as u64 }) } /// Construct a new `Bernoulli` with the probability of success of @@ -91,19 +94,16 @@ impl Bernoulli { /// /// If `numerator == denominator` then the returned `Bernoulli` will always /// return `true`. If `numerator == 0` it will always return `false`. - /// - /// # Panics - /// - /// If `denominator == 0` or `numerator > denominator`. - /// #[inline] - pub fn from_ratio(numerator: u32, denominator: u32) -> Bernoulli { - assert!(numerator <= denominator); + pub fn from_ratio(numerator: u32, denominator: u32) -> Result<Bernoulli, BernoulliError> { + if numerator > denominator { + return Err(BernoulliError::InvalidProbability); + } if numerator == denominator { - return Bernoulli { p_int: ::core::u64::MAX } + return Ok(Bernoulli { p_int: ALWAYS_TRUE }) } - let p_int = ((numerator as f64 / denominator as f64) * SCALE) as u64; - Bernoulli { p_int } + let p_int = ((f64::from(numerator) / f64::from(denominator)) * SCALE) as u64; + Ok(Bernoulli { p_int }) } } @@ -119,15 +119,15 @@ impl Distribution<bool> for Bernoulli { #[cfg(test)] mod test { - use Rng; - use distributions::Distribution; + use crate::Rng; + use crate::distributions::Distribution; use super::Bernoulli; #[test] fn test_trivial() { - let mut r = ::test::rng(1); - let always_false = Bernoulli::new(0.0); - let always_true = Bernoulli::new(1.0); + let mut r = crate::test::rng(1); + let always_false = Bernoulli::new(0.0).unwrap(); + let always_true = Bernoulli::new(1.0).unwrap(); for _ in 0..5 { assert_eq!(r.sample::<bool, _>(&always_false), false); assert_eq!(r.sample::<bool, _>(&always_true), true); @@ -137,17 +137,18 @@ mod test { } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_average() { const P: f64 = 0.3; const NUM: u32 = 3; const DENOM: u32 = 10; - let d1 = Bernoulli::new(P); - let d2 = Bernoulli::from_ratio(NUM, DENOM); + let d1 = Bernoulli::new(P).unwrap(); + let d2 = Bernoulli::from_ratio(NUM, DENOM).unwrap(); const N: u32 = 100_000; let mut sum1: u32 = 0; let mut sum2: u32 = 0; - let mut rng = ::test::rng(2); + let mut rng = crate::test::rng(2); for _ in 0..N { if d1.sample(&mut rng) { sum1 += 1; diff --git a/rand/src/distributions/binomial.rs b/rand/src/distributions/binomial.rs index 2df393e..8fc290a 100644 --- a/rand/src/distributions/binomial.rs +++ b/rand/src/distributions/binomial.rs @@ -8,25 +8,17 @@ // except according to those terms. //! The binomial distribution. +#![allow(deprecated)] +#![allow(clippy::all)] -use Rng; -use distributions::{Distribution, Bernoulli, Cauchy}; -use distributions::utils::log_gamma; +use crate::Rng; +use crate::distributions::{Distribution, Uniform}; /// The binomial distribution `Binomial(n, p)`. /// /// This distribution has density function: /// `f(k) = n!/(k! (n-k)!) p^k (1-p)^(n-k)` for `k >= 0`. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{Binomial, Distribution}; -/// -/// let bin = Binomial::new(20, 0.3); -/// let v = bin.sample(&mut rand::thread_rng()); -/// println!("{} is from a binomial distribution", v); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Binomial { /// Number of trials. @@ -47,6 +39,13 @@ impl Binomial { } } +/// Convert a `f64` to an `i64`, panicing on overflow. +// In the future (Rust 1.34), this might be replaced with `TryFrom`. +fn f64_to_i64(x: f64) -> i64 { + assert!(x < (::std::i64::MAX as f64)); + x as i64 +} + impl Distribution<u64> for Binomial { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 { // Handle these values directly. @@ -55,83 +54,217 @@ impl Distribution<u64> for Binomial { } else if self.p == 1.0 { return self.n; } - - // For low n, it is faster to sample directly. For both methods, - // performance is independent of p. On Intel Haswell CPU this method - // appears to be faster for approx n < 300. - if self.n < 300 { - let mut result = 0; - let d = Bernoulli::new(self.p); - for _ in 0 .. self.n { - result += rng.sample(d) as u32; - } - return result as u64; - } - - // binomial distribution is symmetrical with respect to p -> 1-p, k -> n-k - // switch p so that it is less than 0.5 - this allows for lower expected values - // we will just invert the result at the end + + // The binomial distribution is symmetrical with respect to p -> 1-p, + // k -> n-k switch p so that it is less than 0.5 - this allows for lower + // expected values we will just invert the result at the end let p = if self.p <= 0.5 { self.p } else { 1.0 - self.p }; - // prepare some cached values - let float_n = self.n as f64; - let ln_fact_n = log_gamma(float_n + 1.0); - let pc = 1.0 - p; - let log_p = p.ln(); - let log_pc = pc.ln(); - let expected = self.n as f64 * p; - let sq = (expected * (2.0 * pc)).sqrt(); - - let mut lresult; - - // we use the Cauchy distribution as the comparison distribution - // f(x) ~ 1/(1+x^2) - let cauchy = Cauchy::new(0.0, 1.0); - loop { - let mut comp_dev: f64; + let result; + let q = 1. - p; + + // For small n * min(p, 1 - p), the BINV algorithm based on the inverse + // transformation of the binomial distribution is efficient. Otherwise, + // the BTPE algorithm is used. + // + // Voratas Kachitvichyanukul and Bruce W. Schmeiser. 1988. Binomial + // random variate generation. Commun. ACM 31, 2 (February 1988), + // 216-222. http://dx.doi.org/10.1145/42372.42381 + + // Threshold for prefering the BINV algorithm. The paper suggests 10, + // Ranlib uses 30, and GSL uses 14. + const BINV_THRESHOLD: f64 = 10.; + + if (self.n as f64) * p < BINV_THRESHOLD && + self.n <= (::std::i32::MAX as u64) { + // Use the BINV algorithm. + let s = p / q; + let a = ((self.n + 1) as f64) * s; + let mut r = q.powi(self.n as i32); + let mut u: f64 = rng.gen(); + let mut x = 0; + while u > r as f64 { + u -= r; + x += 1; + r *= a / (x as f64) - s; + } + result = x; + } else { + // Use the BTPE algorithm. + + // Threshold for using the squeeze algorithm. This can be freely + // chosen based on performance. Ranlib and GSL use 20. + const SQUEEZE_THRESHOLD: i64 = 20; + + // Step 0: Calculate constants as functions of `n` and `p`. + let n = self.n as f64; + let np = n * p; + let npq = np * q; + let f_m = np + p; + let m = f64_to_i64(f_m); + // radius of triangle region, since height=1 also area of region + let p1 = (2.195 * npq.sqrt() - 4.6 * q).floor() + 0.5; + // tip of triangle + let x_m = (m as f64) + 0.5; + // left edge of triangle + let x_l = x_m - p1; + // right edge of triangle + let x_r = x_m + p1; + let c = 0.134 + 20.5 / (15.3 + (m as f64)); + // p1 + area of parallelogram region + let p2 = p1 * (1. + 2. * c); + + fn lambda(a: f64) -> f64 { + a * (1. + 0.5 * a) + } + + let lambda_l = lambda((f_m - x_l) / (f_m - x_l * p)); + let lambda_r = lambda((x_r - f_m) / (x_r * q)); + // p1 + area of left tail + let p3 = p2 + c / lambda_l; + // p1 + area of right tail + let p4 = p3 + c / lambda_r; + + // return value + let mut y: i64; + + let gen_u = Uniform::new(0., p4); + let gen_v = Uniform::new(0., 1.); + loop { - // draw from the Cauchy distribution - comp_dev = rng.sample(cauchy); - // shift the peak of the comparison ditribution - lresult = expected + sq * comp_dev; - // repeat the drawing until we are in the range of possible values - if lresult >= 0.0 && lresult < float_n + 1.0 { + // Step 1: Generate `u` for selecting the region. If region 1 is + // selected, generate a triangularly distributed variate. + let u = gen_u.sample(rng); + let mut v = gen_v.sample(rng); + if !(u > p1) { + y = f64_to_i64(x_m - p1 * v + u); break; } - } - // the result should be discrete - lresult = lresult.floor(); + if !(u > p2) { + // Step 2: Region 2, parallelograms. Check if region 2 is + // used. If so, generate `y`. + let x = x_l + (u - p1) / c; + v = v * c + 1.0 - (x - x_m).abs() / p1; + if v > 1. { + continue; + } else { + y = f64_to_i64(x); + } + } else if !(u > p3) { + // Step 3: Region 3, left exponential tail. + y = f64_to_i64(x_l + v.ln() / lambda_l); + if y < 0 { + continue; + } else { + v *= (u - p2) * lambda_l; + } + } else { + // Step 4: Region 4, right exponential tail. + y = f64_to_i64(x_r - v.ln() / lambda_r); + if y > 0 && (y as u64) > self.n { + continue; + } else { + v *= (u - p3) * lambda_r; + } + } + + // Step 5: Acceptance/rejection comparison. + + // Step 5.0: Test for appropriate method of evaluating f(y). + let k = (y - m).abs(); + if !(k > SQUEEZE_THRESHOLD && (k as f64) < 0.5 * npq - 1.) { + // Step 5.1: Evaluate f(y) via the recursive relationship. Start the + // search from the mode. + let s = p / q; + let a = s * (n + 1.); + let mut f = 1.0; + if m < y { + let mut i = m; + loop { + i += 1; + f *= a / (i as f64) - s; + if i == y { + break; + } + } + } else if m > y { + let mut i = y; + loop { + i += 1; + f /= a / (i as f64) - s; + if i == m { + break; + } + } + } + if v > f { + continue; + } else { + break; + } + } - let log_binomial_dist = ln_fact_n - log_gamma(lresult+1.0) - - log_gamma(float_n - lresult + 1.0) + lresult*log_p + (float_n - lresult)*log_pc; - // this is the binomial probability divided by the comparison probability - // we will generate a uniform random value and if it is larger than this, - // we interpret it as a value falling out of the distribution and repeat - let comparison_coeff = (log_binomial_dist.exp() * sq) * (1.2 * (1.0 + comp_dev*comp_dev)); + // Step 5.2: Squeezing. Check the value of ln(v) againts upper and + // lower bound of ln(f(y)). + let k = k as f64; + let rho = (k / npq) * ((k * (k / 3. + 0.625) + 1./6.) / npq + 0.5); + let t = -0.5 * k*k / npq; + let alpha = v.ln(); + if alpha < t - rho { + break; + } + if alpha > t + rho { + continue; + } + + // Step 5.3: Final acceptance/rejection test. + let x1 = (y + 1) as f64; + let f1 = (m + 1) as f64; + let z = (f64_to_i64(n) + 1 - m) as f64; + let w = (f64_to_i64(n) - y + 1) as f64; + + fn stirling(a: f64) -> f64 { + let a2 = a * a; + (13860. - (462. - (132. - (99. - 140. / a2) / a2) / a2) / a2) / a / 166320. + } + + if alpha > x_m * (f1 / x1).ln() + + (n - (m as f64) + 0.5) * (z / w).ln() + + ((y - m) as f64) * (w * p / (x1 * q)).ln() + // We use the signs from the GSL implementation, which are + // different than the ones in the reference. According to + // the GSL authors, the new signs were verified to be + // correct by one of the original designers of the + // algorithm. + + stirling(f1) + stirling(z) - stirling(x1) - stirling(w) + { + continue; + } - if comparison_coeff >= rng.gen() { break; } + assert!(y >= 0); + result = y as u64; } - // invert the result for p < 0.5 + // Invert the result for p < 0.5. if p != self.p { - self.n - lresult as u64 + self.n - result } else { - lresult as u64 + result } } } #[cfg(test)] mod test { - use Rng; - use distributions::Distribution; + use crate::Rng; + use crate::distributions::Distribution; use super::Binomial; fn test_binomial_mean_and_variance<R: Rng>(n: u64, p: f64, rng: &mut R) { @@ -144,17 +277,20 @@ mod test { for i in results.iter_mut() { *i = binomial.sample(rng) as f64; } let mean = results.iter().sum::<f64>() / results.len() as f64; - assert!((mean as f64 - expected_mean).abs() < expected_mean / 50.0); + assert!((mean as f64 - expected_mean).abs() < expected_mean / 50.0, + "mean: {}, expected_mean: {}", mean, expected_mean); let variance = results.iter().map(|x| (x - mean) * (x - mean)).sum::<f64>() / results.len() as f64; - assert!((variance - expected_variance).abs() < expected_variance / 10.0); + assert!((variance - expected_variance).abs() < expected_variance / 10.0, + "variance: {}, expected_variance: {}", variance, expected_variance); } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_binomial() { - let mut rng = ::test::rng(351); + let mut rng = crate::test::rng(351); test_binomial_mean_and_variance(150, 0.1, &mut rng); test_binomial_mean_and_variance(70, 0.6, &mut rng); test_binomial_mean_and_variance(40, 0.5, &mut rng); @@ -164,7 +300,7 @@ mod test { #[test] fn test_binomial_end_points() { - let mut rng = ::test::rng(352); + let mut rng = crate::test::rng(352); assert_eq!(rng.sample(Binomial::new(20, 0.0)), 0); assert_eq!(rng.sample(Binomial::new(20, 1.0)), 20); } diff --git a/rand/src/distributions/cauchy.rs b/rand/src/distributions/cauchy.rs index feef015..0a5d149 100644 --- a/rand/src/distributions/cauchy.rs +++ b/rand/src/distributions/cauchy.rs @@ -8,25 +8,18 @@ // except according to those terms. //! The Cauchy distribution. +#![allow(deprecated)] +#![allow(clippy::all)] -use Rng; -use distributions::Distribution; +use crate::Rng; +use crate::distributions::Distribution; use std::f64::consts::PI; /// The Cauchy distribution `Cauchy(median, scale)`. /// /// This distribution has a density function: /// `f(x) = 1 / (pi * scale * (1 + ((x - median) / scale)^2))` -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{Cauchy, Distribution}; -/// -/// let cau = Cauchy::new(2.0, 5.0); -/// let v = cau.sample(&mut rand::thread_rng()); -/// println!("{} is from a Cauchy(2, 5) distribution", v); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Cauchy { median: f64, @@ -61,7 +54,7 @@ impl Distribution<f64> for Cauchy { #[cfg(test)] mod test { - use distributions::Distribution; + use crate::distributions::Distribution; use super::Cauchy; fn median(mut numbers: &mut [f64]) -> f64 { @@ -75,30 +68,25 @@ mod test { } #[test] - fn test_cauchy_median() { + #[cfg(not(miri))] // Miri doesn't support transcendental functions + fn test_cauchy_averages() { + // NOTE: given that the variance and mean are undefined, + // this test does not have any rigorous statistical meaning. let cauchy = Cauchy::new(10.0, 5.0); - let mut rng = ::test::rng(123); + let mut rng = crate::test::rng(123); let mut numbers: [f64; 1000] = [0.0; 1000]; + let mut sum = 0.0; for i in 0..1000 { numbers[i] = cauchy.sample(&mut rng); + sum += numbers[i]; } let median = median(&mut numbers); println!("Cauchy median: {}", median); - assert!((median - 10.0).abs() < 0.5); // not 100% certain, but probable enough - } - - #[test] - fn test_cauchy_mean() { - let cauchy = Cauchy::new(10.0, 5.0); - let mut rng = ::test::rng(123); - let mut sum = 0.0; - for _ in 0..1000 { - sum += cauchy.sample(&mut rng); - } + assert!((median - 10.0).abs() < 0.4); // not 100% certain, but probable enough let mean = sum / 1000.0; println!("Cauchy mean: {}", mean); // for a Cauchy distribution the mean should not converge - assert!((mean - 10.0).abs() > 0.5); // not 100% certain, but probable enough + assert!((mean - 10.0).abs() > 0.4); // not 100% certain, but probable enough } #[test] diff --git a/rand/src/distributions/dirichlet.rs b/rand/src/distributions/dirichlet.rs index 19384b8..1ce01fd 100644 --- a/rand/src/distributions/dirichlet.rs +++ b/rand/src/distributions/dirichlet.rs @@ -8,28 +8,19 @@ // except according to those terms. //! The dirichlet distribution. +#![allow(deprecated)] +#![allow(clippy::all)] -use Rng; -use distributions::Distribution; -use distributions::gamma::Gamma; +use crate::Rng; +use crate::distributions::Distribution; +use crate::distributions::gamma::Gamma; /// The dirichelet distribution `Dirichlet(alpha)`. /// /// The Dirichlet distribution is a family of continuous multivariate /// probability distributions parameterized by a vector alpha of positive reals. /// It is a multivariate generalization of the beta distribution. -/// -/// # Example -/// -/// ``` -/// use rand::prelude::*; -/// use rand::distributions::Dirichlet; -/// -/// let dirichlet = Dirichlet::new(vec![1.0, 2.0, 3.0]); -/// let samples = dirichlet.sample(&mut rand::thread_rng()); -/// println!("{:?} is from a Dirichlet([1.0, 2.0, 3.0]) distribution", samples); -/// ``` - +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Debug)] pub struct Dirichlet { /// Concentration parameters (alpha) @@ -91,12 +82,12 @@ impl Distribution<Vec<f64>> for Dirichlet { #[cfg(test)] mod test { use super::Dirichlet; - use distributions::Distribution; + use crate::distributions::Distribution; #[test] fn test_dirichlet() { let d = Dirichlet::new(vec![1.0, 2.0, 3.0]); - let mut rng = ::test::rng(221); + let mut rng = crate::test::rng(221); let samples = d.sample(&mut rng); let _: Vec<f64> = samples .into_iter() @@ -112,7 +103,7 @@ mod test { let alpha = 0.5f64; let size = 2; let d = Dirichlet::new_with_param(alpha, size); - let mut rng = ::test::rng(221); + let mut rng = crate::test::rng(221); let samples = d.sample(&mut rng); let _: Vec<f64> = samples .into_iter() diff --git a/rand/src/distributions/exponential.rs b/rand/src/distributions/exponential.rs index a7d0500..0278248 100644 --- a/rand/src/distributions/exponential.rs +++ b/rand/src/distributions/exponential.rs @@ -8,10 +8,11 @@ // except according to those terms. //! The exponential distribution. +#![allow(deprecated)] -use {Rng}; -use distributions::{ziggurat_tables, Distribution}; -use distributions::utils::ziggurat; +use crate::{Rng}; +use crate::distributions::{ziggurat_tables, Distribution}; +use crate::distributions::utils::ziggurat; /// Samples floating-point numbers according to the exponential distribution, /// with rate parameter `λ = 1`. This is equivalent to `Exp::new(1.0)` or @@ -27,15 +28,7 @@ use distributions::utils::ziggurat; /// Generate Normal Random Samples*]( /// https://www.doornik.com/research/ziggurat.pdf). /// Nuffield College, Oxford -/// -/// # Example -/// ``` -/// use rand::prelude::*; -/// use rand::distributions::Exp1; -/// -/// let val: f64 = SmallRng::from_entropy().sample(Exp1); -/// println!("{}", val); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Exp1; @@ -64,17 +57,8 @@ impl Distribution<f64> for Exp1 { /// This distribution has density function: `f(x) = lambda * exp(-lambda * x)` /// for `x > 0`. /// -/// Note that [`Exp1`](struct.Exp1.html) is an optimised implementation for `lambda = 1`. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{Exp, Distribution}; -/// -/// let exp = Exp::new(2.0); -/// let v = exp.sample(&mut rand::thread_rng()); -/// println!("{} is from a Exp(2) distribution", v); -/// ``` +/// Note that [`Exp1`](crate::distributions::Exp1) is an optimised implementation for `lambda = 1`. +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Exp { /// `lambda` stored as `1/lambda`, since this is what we scale by. @@ -100,13 +84,13 @@ impl Distribution<f64> for Exp { #[cfg(test)] mod test { - use distributions::Distribution; + use crate::distributions::Distribution; use super::Exp; #[test] fn test_exp() { let exp = Exp::new(10.0); - let mut rng = ::test::rng(221); + let mut rng = crate::test::rng(221); for _ in 0..1000 { assert!(exp.sample(&mut rng) >= 0.0); } diff --git a/rand/src/distributions/float.rs b/rand/src/distributions/float.rs index ece12f5..bda523a 100644 --- a/rand/src/distributions/float.rs +++ b/rand/src/distributions/float.rs @@ -9,9 +9,9 @@ //! Basic floating-point number distributions use core::mem; -use Rng; -use distributions::{Distribution, Standard}; -use distributions::utils::FloatSIMDUtils; +use crate::Rng; +use crate::distributions::{Distribution, Standard}; +use crate::distributions::utils::FloatSIMDUtils; #[cfg(feature="simd_support")] use packed_simd::*; @@ -36,9 +36,9 @@ use packed_simd::*; /// println!("f32 from (0, 1): {}", val); /// ``` /// -/// [`Standard`]: struct.Standard.html -/// [`Open01`]: struct.Open01.html -/// [`Uniform`]: uniform/struct.Uniform.html +/// [`Standard`]: crate::distributions::Standard +/// [`Open01`]: crate::distributions::Open01 +/// [`Uniform`]: crate::distributions::uniform::Uniform #[derive(Clone, Copy, Debug)] pub struct OpenClosed01; @@ -62,14 +62,16 @@ pub struct OpenClosed01; /// println!("f32 from (0, 1): {}", val); /// ``` /// -/// [`Standard`]: struct.Standard.html -/// [`OpenClosed01`]: struct.OpenClosed01.html -/// [`Uniform`]: uniform/struct.Uniform.html +/// [`Standard`]: crate::distributions::Standard +/// [`OpenClosed01`]: crate::distributions::OpenClosed01 +/// [`Uniform`]: crate::distributions::uniform::Uniform #[derive(Clone, Copy, Debug)] pub struct Open01; -pub(crate) trait IntoFloat { +// This trait is needed by both this lib and rand_distr hence is a hidden export +#[doc(hidden)] +pub trait IntoFloat { type F; /// Helper method to combine the fraction and a contant exponent into a @@ -93,9 +95,7 @@ macro_rules! float_impls { // The exponent is encoded using an offset-binary representation let exponent_bits: $u_scalar = (($exponent_bias + exponent) as $u_scalar) << $fraction_bits; - // TODO: use from_bits when min compiler > 1.25 (see #545) - // $ty::from_bits(self | exponent_bits) - unsafe{ mem::transmute(self | exponent_bits) } + $ty::from_bits(self | exponent_bits) } } @@ -168,9 +168,9 @@ float_impls! { f64x8, u64x8, f64, u64, 52, 1023 } #[cfg(test)] mod tests { - use Rng; - use distributions::{Open01, OpenClosed01}; - use rngs::mock::StepRng; + use crate::Rng; + use crate::distributions::{Open01, OpenClosed01}; + use crate::rngs::mock::StepRng; #[cfg(feature="simd_support")] use packed_simd::*; diff --git a/rand/src/distributions/gamma.rs b/rand/src/distributions/gamma.rs index 43ac2bc..b5a97f5 100644 --- a/rand/src/distributions/gamma.rs +++ b/rand/src/distributions/gamma.rs @@ -8,13 +8,14 @@ // except according to those terms. //! The Gamma and derived distributions. +#![allow(deprecated)] use self::GammaRepr::*; use self::ChiSquaredRepr::*; -use Rng; -use distributions::normal::StandardNormal; -use distributions::{Distribution, Exp, Open01}; +use crate::Rng; +use crate::distributions::normal::StandardNormal; +use crate::distributions::{Distribution, Exp, Open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -32,20 +33,11 @@ use distributions::{Distribution, Exp, Open01}; /// == 1`, and using the boosting technique described in that paper for /// `shape < 1`. /// -/// # Example -/// -/// ``` -/// use rand::distributions::{Distribution, Gamma}; -/// -/// let gamma = Gamma::new(2.0, 5.0); -/// let v = gamma.sample(&mut rand::thread_rng()); -/// println!("{} is from a Gamma(2, 5) distribution", v); -/// ``` -/// /// [^1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method for /// Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3 /// (September 2000), 363-372. /// DOI:[10.1145/358407.358414](https://doi.acm.org/10.1145/358407.358414) +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Gamma { repr: GammaRepr, @@ -174,16 +166,7 @@ impl Distribution<f64> for GammaLargeShape { /// of `k` independent standard normal random variables. For other /// `k`, this uses the equivalent characterisation /// `χ²(k) = Gamma(k/2, 2)`. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{ChiSquared, Distribution}; -/// -/// let chi = ChiSquared::new(11.0); -/// let v = chi.sample(&mut rand::thread_rng()); -/// println!("{} is from a χ²(11) distribution", v) -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct ChiSquared { repr: ChiSquaredRepr, @@ -229,16 +212,7 @@ impl Distribution<f64> for ChiSquared { /// This distribution is equivalent to the ratio of two normalised /// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) / /// (χ²(n)/n)`. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{FisherF, Distribution}; -/// -/// let f = FisherF::new(2.0, 32.0); -/// let v = f.sample(&mut rand::thread_rng()); -/// println!("{} is from an F(2, 32) distribution", v) -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct FisherF { numer: ChiSquared, @@ -270,16 +244,7 @@ impl Distribution<f64> for FisherF { /// The Student t distribution, `t(nu)`, where `nu` is the degrees of /// freedom. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{StudentT, Distribution}; -/// -/// let t = StudentT::new(11.0); -/// let v = t.sample(&mut rand::thread_rng()); -/// println!("{} is from a t(11) distribution", v) -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct StudentT { chi: ChiSquared, @@ -305,16 +270,7 @@ impl Distribution<f64> for StudentT { } /// The Beta distribution with shape parameters `alpha` and `beta`. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{Distribution, Beta}; -/// -/// let beta = Beta::new(2.0, 5.0); -/// let v = beta.sample(&mut rand::thread_rng()); -/// println!("{} is from a Beta(2, 5) distribution", v); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Beta { gamma_a: Gamma, @@ -345,30 +301,32 @@ impl Distribution<f64> for Beta { #[cfg(test)] mod test { - use distributions::Distribution; + use crate::distributions::Distribution; use super::{Beta, ChiSquared, StudentT, FisherF}; + const N: u32 = 100; + #[test] fn test_chi_squared_one() { let chi = ChiSquared::new(1.0); - let mut rng = ::test::rng(201); - for _ in 0..1000 { + let mut rng = crate::test::rng(201); + for _ in 0..N { chi.sample(&mut rng); } } #[test] fn test_chi_squared_small() { let chi = ChiSquared::new(0.5); - let mut rng = ::test::rng(202); - for _ in 0..1000 { + let mut rng = crate::test::rng(202); + for _ in 0..N { chi.sample(&mut rng); } } #[test] fn test_chi_squared_large() { let chi = ChiSquared::new(30.0); - let mut rng = ::test::rng(203); - for _ in 0..1000 { + let mut rng = crate::test::rng(203); + for _ in 0..N { chi.sample(&mut rng); } } @@ -381,8 +339,8 @@ mod test { #[test] fn test_f() { let f = FisherF::new(2.0, 32.0); - let mut rng = ::test::rng(204); - for _ in 0..1000 { + let mut rng = crate::test::rng(204); + for _ in 0..N { f.sample(&mut rng); } } @@ -390,8 +348,8 @@ mod test { #[test] fn test_t() { let t = StudentT::new(11.0); - let mut rng = ::test::rng(205); - for _ in 0..1000 { + let mut rng = crate::test::rng(205); + for _ in 0..N { t.sample(&mut rng); } } @@ -399,8 +357,8 @@ mod test { #[test] fn test_beta() { let beta = Beta::new(1.0, 2.0); - let mut rng = ::test::rng(201); - for _ in 0..1000 { + let mut rng = crate::test::rng(201); + for _ in 0..N { beta.sample(&mut rng); } } diff --git a/rand/src/distributions/integer.rs b/rand/src/distributions/integer.rs index 7e408db..5238339 100644 --- a/rand/src/distributions/integer.rs +++ b/rand/src/distributions/integer.rs @@ -8,8 +8,10 @@ //! The implementations of the `Standard` distribution for integer types. -use {Rng}; -use distributions::{Distribution, Standard}; +use crate::{Rng}; +use crate::distributions::{Distribution, Standard}; +use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize}; +#[cfg(not(target_os = "emscripten"))] use core::num::NonZeroU128; #[cfg(feature="simd_support")] use packed_simd::*; #[cfg(all(target_arch = "x86", feature="nightly"))] @@ -45,13 +47,13 @@ impl Distribution<u64> for Standard { } } -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] +#[cfg(not(target_os = "emscripten"))] impl Distribution<u128> for Standard { #[inline] fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u128 { // Use LE; we explicitly generate one value before the next. - let x = rng.next_u64() as u128; - let y = rng.next_u64() as u128; + let x = u128::from(rng.next_u64()); + let y = u128::from(rng.next_u64()); (y << 64) | x } } @@ -85,9 +87,30 @@ impl_int_from_uint! { i8, u8 } impl_int_from_uint! { i16, u16 } impl_int_from_uint! { i32, u32 } impl_int_from_uint! { i64, u64 } -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] impl_int_from_uint! { i128, u128 } +#[cfg(not(target_os = "emscripten"))] impl_int_from_uint! { i128, u128 } impl_int_from_uint! { isize, usize } +macro_rules! impl_nzint { + ($ty:ty, $new:path) => { + impl Distribution<$ty> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty { + loop { + if let Some(nz) = $new(rng.gen()) { + break nz; + } + } + } + } + } +} + +impl_nzint!(NonZeroU8, NonZeroU8::new); +impl_nzint!(NonZeroU16, NonZeroU16::new); +impl_nzint!(NonZeroU32, NonZeroU32::new); +impl_nzint!(NonZeroU64, NonZeroU64::new); +#[cfg(not(target_os = "emscripten"))] impl_nzint!(NonZeroU128, NonZeroU128::new); +impl_nzint!(NonZeroUsize, NonZeroUsize::new); + #[cfg(feature="simd_support")] macro_rules! simd_impl { ($(($intrinsic:ident, $vec:ty),)+) => {$( @@ -135,19 +158,19 @@ simd_impl!((__m64, u8x8), (__m128i, u8x16), (__m256i, u8x32),); #[cfg(test)] mod tests { - use Rng; - use distributions::{Standard}; + use crate::Rng; + use crate::distributions::{Standard}; #[test] fn test_integers() { - let mut rng = ::test::rng(806); + let mut rng = crate::test::rng(806); rng.sample::<isize, _>(Standard); rng.sample::<i8, _>(Standard); rng.sample::<i16, _>(Standard); rng.sample::<i32, _>(Standard); rng.sample::<i64, _>(Standard); - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] + #[cfg(not(target_os = "emscripten"))] rng.sample::<i128, _>(Standard); rng.sample::<usize, _>(Standard); @@ -155,7 +178,7 @@ mod tests { rng.sample::<u16, _>(Standard); rng.sample::<u32, _>(Standard); rng.sample::<u64, _>(Standard); - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] + #[cfg(not(target_os = "emscripten"))] rng.sample::<u128, _>(Standard); } } diff --git a/rand/src/distributions/mod.rs b/rand/src/distributions/mod.rs index 5e879cb..02ece6f 100644 --- a/rand/src/distributions/mod.rs +++ b/rand/src/distributions/mod.rs @@ -7,12 +7,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Generating random samples from probability distributions. +//! Generating random samples from probability distributions //! //! This module is the home of the [`Distribution`] trait and several of its //! implementations. It is the workhorse behind some of the convenient -//! functionality of the [`Rng`] trait, including [`gen`], [`gen_range`] and -//! of course [`sample`]. +//! functionality of the [`Rng`] trait, e.g. [`Rng::gen`], [`Rng::gen_range`] and +//! of course [`Rng::sample`]. //! //! Abstractly, a [probability distribution] describes the probability of //! occurance of each value in its sample space. @@ -40,8 +40,14 @@ //! possible to generate type `T` with [`Rng::gen()`], and by extension also //! with the [`random()`] function. //! +//! ## Random characters +//! +//! [`Alphanumeric`] is a simple distribution to sample random letters and +//! numbers of the `char` type; in contrast [`Standard`] may sample any valid +//! `char`. +//! //! -//! # Distribution to sample from a `Uniform` range +//! # Uniform numeric ranges //! //! The [`Uniform`] distribution is more flexible than [`Standard`], but also //! more specialised: it supports fewer target types, but allows the sample @@ -56,158 +62,84 @@ //! //! User types `T` may also implement `Distribution<T>` for [`Uniform`], //! although this is less straightforward than for [`Standard`] (see the -//! documentation in the [`uniform` module]. Doing so enables generation of +//! documentation in the [`uniform`] module. Doing so enables generation of //! values of type `T` with [`Rng::gen_range`]. //! -//! -//! # Other distributions +//! ## Open and half-open ranges //! //! There are surprisingly many ways to uniformly generate random floats. A //! range between 0 and 1 is standard, but the exact bounds (open vs closed) //! and accuracy differ. In addition to the [`Standard`] distribution Rand offers -//! [`Open01`] and [`OpenClosed01`]. See [Floating point implementation] for -//! more details. -//! -//! [`Alphanumeric`] is a simple distribution to sample random letters and -//! numbers of the `char` type; in contrast [`Standard`] may sample any valid -//! `char`. -//! -//! [`WeightedIndex`] can be used to do weighted sampling from a set of items, -//! such as from an array. -//! -//! # Non-uniform probability distributions -//! -//! Rand currently provides the following probability distributions: -//! -//! - Related to real-valued quantities that grow linearly -//! (e.g. errors, offsets): -//! - [`Normal`] distribution, and [`StandardNormal`] as a primitive -//! - [`Cauchy`] distribution -//! - Related to Bernoulli trials (yes/no events, with a given probability): -//! - [`Binomial`] distribution -//! - [`Bernoulli`] distribution, similar to [`Rng::gen_bool`]. -//! - Related to positive real-valued quantities that grow exponentially -//! (e.g. prices, incomes, populations): -//! - [`LogNormal`] distribution -//! - Related to the occurrence of independent events at a given rate: -//! - [`Pareto`] distribution -//! - [`Poisson`] distribution -//! - [`Exp`]onential distribution, and [`Exp1`] as a primitive -//! - [`Weibull`] distribution -//! - Gamma and derived distributions: -//! - [`Gamma`] distribution -//! - [`ChiSquared`] distribution -//! - [`StudentT`] distribution -//! - [`FisherF`] distribution -//! - Triangular distribution: -//! - [`Beta`] distribution -//! - [`Triangular`] distribution -//! - Multivariate probability distributions -//! - [`Dirichlet`] distribution -//! - [`UnitSphereSurface`] distribution -//! - [`UnitCircle`] distribution +//! [`Open01`] and [`OpenClosed01`]. See "Floating point implementation" section of +//! [`Standard`] documentation for more details. //! -//! # Examples +//! # Non-uniform sampling //! -//! Sampling from a distribution: +//! Sampling a simple true/false outcome with a given probability has a name: +//! the [`Bernoulli`] distribution (this is used by [`Rng::gen_bool`]). //! -//! ``` -//! use rand::{thread_rng, Rng}; -//! use rand::distributions::Exp; +//! For weighted sampling from a sequence of discrete values, use the +//! [`weighted`] module. //! -//! let exp = Exp::new(2.0); -//! let v = thread_rng().sample(exp); -//! println!("{} is from an Exp(2) distribution", v); -//! ``` -//! -//! Implementing the [`Standard`] distribution for a user type: -//! -//! ``` -//! # #![allow(dead_code)] -//! use rand::Rng; -//! use rand::distributions::{Distribution, Standard}; -//! -//! struct MyF32 { -//! x: f32, -//! } -//! -//! impl Distribution<MyF32> for Standard { -//! fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> MyF32 { -//! MyF32 { x: rng.gen() } -//! } -//! } -//! ``` +//! This crate no longer includes other non-uniform distributions; instead +//! it is recommended that you use either [`rand_distr`] or [`statrs`]. //! //! //! [probability distribution]: https://en.wikipedia.org/wiki/Probability_distribution -//! [`Distribution`]: trait.Distribution.html -//! [`gen_range`]: ../trait.Rng.html#method.gen_range -//! [`gen`]: ../trait.Rng.html#method.gen -//! [`sample`]: ../trait.Rng.html#method.sample -//! [`new_inclusive`]: struct.Uniform.html#method.new_inclusive -//! [`random()`]: ../fn.random.html -//! [`Rng::gen_bool`]: ../trait.Rng.html#method.gen_bool -//! [`Rng::gen_range`]: ../trait.Rng.html#method.gen_range -//! [`Rng::gen()`]: ../trait.Rng.html#method.gen -//! [`Rng`]: ../trait.Rng.html -//! [`uniform` module]: uniform/index.html -//! [Floating point implementation]: struct.Standard.html#floating-point-implementation -// distributions -//! [`Alphanumeric`]: struct.Alphanumeric.html -//! [`Bernoulli`]: struct.Bernoulli.html -//! [`Beta`]: struct.Beta.html -//! [`Binomial`]: struct.Binomial.html -//! [`Cauchy`]: struct.Cauchy.html -//! [`ChiSquared`]: struct.ChiSquared.html -//! [`Dirichlet`]: struct.Dirichlet.html -//! [`Exp`]: struct.Exp.html -//! [`Exp1`]: struct.Exp1.html -//! [`FisherF`]: struct.FisherF.html -//! [`Gamma`]: struct.Gamma.html -//! [`LogNormal`]: struct.LogNormal.html -//! [`Normal`]: struct.Normal.html -//! [`Open01`]: struct.Open01.html -//! [`OpenClosed01`]: struct.OpenClosed01.html -//! [`Pareto`]: struct.Pareto.html -//! [`Poisson`]: struct.Poisson.html -//! [`Standard`]: struct.Standard.html -//! [`StandardNormal`]: struct.StandardNormal.html -//! [`StudentT`]: struct.StudentT.html -//! [`Triangular`]: struct.Triangular.html -//! [`Uniform`]: struct.Uniform.html -//! [`Uniform::new`]: struct.Uniform.html#method.new -//! [`Uniform::new_inclusive`]: struct.Uniform.html#method.new_inclusive -//! [`UnitSphereSurface`]: struct.UnitSphereSurface.html -//! [`UnitCircle`]: struct.UnitCircle.html -//! [`Weibull`]: struct.Weibull.html -//! [`WeightedIndex`]: struct.WeightedIndex.html +//! [`rand_distr`]: https://crates.io/crates/rand_distr +//! [`statrs`]: https://crates.io/crates/statrs + +//! [`Alphanumeric`]: distributions::Alphanumeric +//! [`Bernoulli`]: distributions::Bernoulli +//! [`Open01`]: distributions::Open01 +//! [`OpenClosed01`]: distributions::OpenClosed01 +//! [`Standard`]: distributions::Standard +//! [`Uniform`]: distributions::Uniform +//! [`Uniform::new`]: distributions::Uniform::new +//! [`Uniform::new_inclusive`]: distributions::Uniform::new_inclusive +//! [`weighted`]: distributions::weighted +//! [`rand_distr`]: https://crates.io/crates/rand_distr +//! [`statrs`]: https://crates.io/crates/statrs -#[cfg(any(rustc_1_26, features="nightly"))] use core::iter; -use Rng; +use crate::Rng; pub use self::other::Alphanumeric; #[doc(inline)] pub use self::uniform::Uniform; pub use self::float::{OpenClosed01, Open01}; -pub use self::bernoulli::Bernoulli; +pub use self::bernoulli::{Bernoulli, BernoulliError}; #[cfg(feature="alloc")] pub use self::weighted::{WeightedIndex, WeightedError}; + +// The following are all deprecated after being moved to rand_distr +#[allow(deprecated)] #[cfg(feature="std")] pub use self::unit_sphere::UnitSphereSurface; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::unit_circle::UnitCircle; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT, Beta}; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::normal::{Normal, LogNormal, StandardNormal}; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::exponential::{Exp, Exp1}; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::pareto::Pareto; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::poisson::Poisson; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::binomial::Binomial; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::cauchy::Cauchy; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::dirichlet::Dirichlet; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::triangular::Triangular; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::weibull::Weibull; pub mod uniform; mod bernoulli; -#[cfg(feature="alloc")] mod weighted; +#[cfg(feature="alloc")] pub mod weighted; #[cfg(feature="std")] mod unit_sphere; #[cfg(feature="std")] mod unit_circle; #[cfg(feature="std")] mod gamma; @@ -222,6 +154,9 @@ mod bernoulli; #[cfg(feature="std")] mod weibull; mod float; +#[doc(hidden)] pub mod hidden_export { + pub use super::float::IntoFloat; // used by rand_distr +} mod integer; mod other; mod utils; @@ -238,8 +173,7 @@ mod utils; /// advantage of not needing to consider thread safety, and for most /// distributions efficient state-less sampling algorithms are available. /// -/// [`Rng`]: ../trait.Rng.html -/// [`sample_iter`]: trait.Distribution.html#method.sample_iter +/// [`sample_iter`]: Distribution::method.sample_iter pub trait Distribution<T> { /// Generate a random value of `T`, using `rng` as the source of randomness. fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> T; @@ -247,33 +181,39 @@ pub trait Distribution<T> { /// Create an iterator that generates random values of `T`, using `rng` as /// the source of randomness. /// + /// Note that this function takes `self` by value. This works since + /// `Distribution<T>` is impl'd for `&D` where `D: Distribution<T>`, + /// however borrowing is not automatic hence `distr.sample_iter(...)` may + /// need to be replaced with `(&distr).sample_iter(...)` to borrow or + /// `(&*distr).sample_iter(...)` to reborrow an existing reference. + /// /// # Example /// /// ``` /// use rand::thread_rng; /// use rand::distributions::{Distribution, Alphanumeric, Uniform, Standard}; /// - /// let mut rng = thread_rng(); + /// let rng = thread_rng(); /// /// // Vec of 16 x f32: - /// let v: Vec<f32> = Standard.sample_iter(&mut rng).take(16).collect(); + /// let v: Vec<f32> = Standard.sample_iter(rng).take(16).collect(); /// /// // String: - /// let s: String = Alphanumeric.sample_iter(&mut rng).take(7).collect(); + /// let s: String = Alphanumeric.sample_iter(rng).take(7).collect(); /// /// // Dice-rolling: /// let die_range = Uniform::new_inclusive(1, 6); - /// let mut roll_die = die_range.sample_iter(&mut rng); + /// let mut roll_die = die_range.sample_iter(rng); /// while roll_die.next().unwrap() != 6 { /// println!("Not a 6; rolling again!"); /// } /// ``` - fn sample_iter<'a, R>(&'a self, rng: &'a mut R) -> DistIter<'a, Self, R, T> - where Self: Sized, R: Rng + fn sample_iter<R>(self, rng: R) -> DistIter<Self, R, T> + where R: Rng, Self: Sized { DistIter { distr: self, - rng: rng, + rng, phantom: ::core::marker::PhantomData, } } @@ -292,23 +232,25 @@ impl<'a, T, D: Distribution<T>> Distribution<T> for &'a D { /// This `struct` is created by the [`sample_iter`] method on [`Distribution`]. /// See its documentation for more. /// -/// [`Distribution`]: trait.Distribution.html -/// [`sample_iter`]: trait.Distribution.html#method.sample_iter +/// [`sample_iter`]: Distribution::sample_iter #[derive(Debug)] -pub struct DistIter<'a, D: 'a, R: 'a, T> { - distr: &'a D, - rng: &'a mut R, +pub struct DistIter<D, R, T> { + distr: D, + rng: R, phantom: ::core::marker::PhantomData<T>, } -impl<'a, D, R, T> Iterator for DistIter<'a, D, R, T> - where D: Distribution<T>, R: Rng + 'a +impl<D, R, T> Iterator for DistIter<D, R, T> + where D: Distribution<T>, R: Rng { type Item = T; #[inline(always)] fn next(&mut self) -> Option<T> { - Some(self.distr.sample(self.rng)) + // Here, self.rng may be a reference, but we must take &mut anyway. + // Even if sample could take an R: Rng by value, we would need to do this + // since Rng is not copyable and we cannot enforce that this is "reborrowable". + Some(self.distr.sample(&mut self.rng)) } fn size_hint(&self) -> (usize, Option<usize>) { @@ -316,20 +258,19 @@ impl<'a, D, R, T> Iterator for DistIter<'a, D, R, T> } } -#[cfg(rustc_1_26)] -impl<'a, D, R, T> iter::FusedIterator for DistIter<'a, D, R, T> - where D: Distribution<T>, R: Rng + 'a {} +impl<D, R, T> iter::FusedIterator for DistIter<D, R, T> + where D: Distribution<T>, R: Rng {} #[cfg(features = "nightly")] -impl<'a, D, R, T> iter::TrustedLen for DistIter<'a, D, R, T> - where D: Distribution<T>, R: Rng + 'a {} +impl<D, R, T> iter::TrustedLen for DistIter<D, R, T> + where D: Distribution<T>, R: Rng {} /// A generic random value distribution, implemented for many primitive types. /// Usually generates values with a numerically uniform distribution, and with a /// range appropriate to the type. /// -/// ## Built-in Implementations +/// ## Provided implementations /// /// Assuming the provided `Rng` is well-behaved, these implementations /// generate values with the following ranges and distributions: @@ -346,20 +287,42 @@ impl<'a, D, R, T> iter::TrustedLen for DistIter<'a, D, R, T> /// * Wrapping integers (`Wrapping<T>`), besides the type identical to their /// normal integer variants. /// -/// The following aggregate types also implement the distribution `Standard` as -/// long as their component types implement it: +/// The `Standard` distribution also supports generation of the following +/// compound types where all component types are supported: /// -/// * Tuples and arrays: Each element of the tuple or array is generated -/// independently, using the `Standard` distribution recursively. -/// * `Option<T>` where `Standard` is implemented for `T`: Returns `None` with -/// probability 0.5; otherwise generates a random `x: T` and returns `Some(x)`. +/// * Tuples (up to 12 elements): each element is generated sequentially. +/// * Arrays (up to 32 elements): each element is generated sequentially; +/// see also [`Rng::fill`] which supports arbitrary array length for integer +/// types and tends to be faster for `u32` and smaller types. +/// * `Option<T>` first generates a `bool`, and if true generates and returns +/// `Some(value)` where `value: T`, otherwise returning `None`. /// -/// # Example +/// ## Custom implementations +/// +/// The [`Standard`] distribution may be implemented for user types as follows: +/// +/// ``` +/// # #![allow(dead_code)] +/// use rand::Rng; +/// use rand::distributions::{Distribution, Standard}; +/// +/// struct MyF32 { +/// x: f32, +/// } +/// +/// impl Distribution<MyF32> for Standard { +/// fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> MyF32 { +/// MyF32 { x: rng.gen() } +/// } +/// } +/// ``` +/// +/// ## Example usage /// ``` /// use rand::prelude::*; /// use rand::distributions::Standard; /// -/// let val: f32 = SmallRng::from_entropy().sample(Standard); +/// let val: f32 = StdRng::from_entropy().sample(Standard); /// println!("f32 from [0, 1): {}", val); /// ``` /// @@ -379,243 +342,40 @@ impl<'a, D, R, T> iter::TrustedLen for DistIter<'a, D, R, T> /// faster on some architectures (on modern Intel CPUs all methods have /// approximately equal performance). /// -/// [`Open01`]: struct.Open01.html -/// [`OpenClosed01`]: struct.OpenClosed01.html -/// [`Uniform`]: uniform/struct.Uniform.html +/// [`Uniform`]: uniform::Uniform #[derive(Clone, Copy, Debug)] pub struct Standard; -/// A value with a particular weight for use with `WeightedChoice`. -#[deprecated(since="0.6.0", note="use WeightedIndex instead")] -#[allow(deprecated)] -#[derive(Copy, Clone, Debug)] -pub struct Weighted<T> { - /// The numerical weight of this item - pub weight: u32, - /// The actual item which is being weighted - pub item: T, -} - -/// A distribution that selects from a finite collection of weighted items. -/// -/// Deprecated: use [`WeightedIndex`] instead. -/// -/// [`WeightedIndex`]: struct.WeightedIndex.html -#[deprecated(since="0.6.0", note="use WeightedIndex instead")] -#[allow(deprecated)] -#[derive(Debug)] -pub struct WeightedChoice<'a, T:'a> { - items: &'a mut [Weighted<T>], - weight_range: Uniform<u32>, -} - -#[deprecated(since="0.6.0", note="use WeightedIndex instead")] -#[allow(deprecated)] -impl<'a, T: Clone> WeightedChoice<'a, T> { - /// Create a new `WeightedChoice`. - /// - /// Panics if: - /// - /// - `items` is empty - /// - the total weight is 0 - /// - the total weight is larger than a `u32` can contain. - pub fn new(items: &'a mut [Weighted<T>]) -> WeightedChoice<'a, T> { - // strictly speaking, this is subsumed by the total weight == 0 case - assert!(!items.is_empty(), "WeightedChoice::new called with no items"); - - let mut running_total: u32 = 0; - - // we convert the list from individual weights to cumulative - // weights so we can binary search. This *could* drop elements - // with weight == 0 as an optimisation. - for item in items.iter_mut() { - running_total = match running_total.checked_add(item.weight) { - Some(n) => n, - None => panic!("WeightedChoice::new called with a total weight \ - larger than a u32 can contain") - }; - - item.weight = running_total; - } - assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0"); - - WeightedChoice { - items, - // we're likely to be generating numbers in this range - // relatively often, so might as well cache it - weight_range: Uniform::new(0, running_total) - } - } -} - -#[deprecated(since="0.6.0", note="use WeightedIndex instead")] -#[allow(deprecated)] -impl<'a, T: Clone> Distribution<T> for WeightedChoice<'a, T> { - fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> T { - // we want to find the first element that has cumulative - // weight > sample_weight, which we do by binary since the - // cumulative weights of self.items are sorted. - - // choose a weight in [0, total_weight) - let sample_weight = self.weight_range.sample(rng); - - // short circuit when it's the first item - if sample_weight < self.items[0].weight { - return self.items[0].item.clone(); - } - - let mut idx = 0; - let mut modifier = self.items.len(); - - // now we know that every possibility has an element to the - // left, so we can just search for the last element that has - // cumulative weight <= sample_weight, then the next one will - // be "it". (Note that this greatest element will never be the - // last element of the vector, since sample_weight is chosen - // in [0, total_weight) and the cumulative weight of the last - // one is exactly the total weight.) - while modifier > 1 { - let i = idx + modifier / 2; - if self.items[i].weight <= sample_weight { - // we're small, so look to the right, but allow this - // exact element still. - idx = i; - // we need the `/ 2` to round up otherwise we'll drop - // the trailing elements when `modifier` is odd. - modifier += 1; - } else { - // otherwise we're too big, so go left. (i.e. do - // nothing) - } - modifier /= 2; - } - self.items[idx + 1].item.clone() - } -} - -#[cfg(test)] +#[cfg(all(test, feature = "std"))] mod tests { - use rngs::mock::StepRng; - #[allow(deprecated)] - use super::{WeightedChoice, Weighted, Distribution}; + use crate::Rng; + use super::{Distribution, Uniform}; #[test] - #[allow(deprecated)] - fn test_weighted_choice() { - // this makes assumptions about the internal implementation of - // WeightedChoice. It may fail when the implementation in - // `distributions::uniform::UniformInt` changes. - - macro_rules! t { - ($items:expr, $expected:expr) => {{ - let mut items = $items; - let mut total_weight = 0; - for item in &items { total_weight += item.weight; } - - let wc = WeightedChoice::new(&mut items); - let expected = $expected; - - // Use extremely large steps between the random numbers, because - // we test with small ranges and `UniformInt` is designed to prefer - // the most significant bits. - let mut rng = StepRng::new(0, !0 / (total_weight as u64)); - - for &val in expected.iter() { - assert_eq!(wc.sample(&mut rng), val) - } - }} - } - - t!([Weighted { weight: 1, item: 10}], [10]); - - // skip some - t!([Weighted { weight: 0, item: 20}, - Weighted { weight: 2, item: 21}, - Weighted { weight: 0, item: 22}, - Weighted { weight: 1, item: 23}], - [21, 21, 23]); - - // different weights - t!([Weighted { weight: 4, item: 30}, - Weighted { weight: 3, item: 31}], - [30, 31, 30, 31, 30, 31, 30]); - - // check that we're binary searching - // correctly with some vectors of odd - // length. - t!([Weighted { weight: 1, item: 40}, - Weighted { weight: 1, item: 41}, - Weighted { weight: 1, item: 42}, - Weighted { weight: 1, item: 43}, - Weighted { weight: 1, item: 44}], - [40, 41, 42, 43, 44]); - t!([Weighted { weight: 1, item: 50}, - Weighted { weight: 1, item: 51}, - Weighted { weight: 1, item: 52}, - Weighted { weight: 1, item: 53}, - Weighted { weight: 1, item: 54}, - Weighted { weight: 1, item: 55}, - Weighted { weight: 1, item: 56}], - [50, 54, 51, 55, 52, 56, 53]); - } - - #[test] - #[allow(deprecated)] - fn test_weighted_clone_initialization() { - let initial : Weighted<u32> = Weighted {weight: 1, item: 1}; - let clone = initial.clone(); - assert_eq!(initial.weight, clone.weight); - assert_eq!(initial.item, clone.item); - } - - #[test] #[should_panic] - #[allow(deprecated)] - fn test_weighted_clone_change_weight() { - let initial : Weighted<u32> = Weighted {weight: 1, item: 1}; - let mut clone = initial.clone(); - clone.weight = 5; - assert_eq!(initial.weight, clone.weight); - } - - #[test] #[should_panic] - #[allow(deprecated)] - fn test_weighted_clone_change_item() { - let initial : Weighted<u32> = Weighted {weight: 1, item: 1}; - let mut clone = initial.clone(); - clone.item = 5; - assert_eq!(initial.item, clone.item); - - } - - #[test] #[should_panic] - #[allow(deprecated)] - fn test_weighted_choice_no_items() { - WeightedChoice::<isize>::new(&mut []); - } - #[test] #[should_panic] - #[allow(deprecated)] - fn test_weighted_choice_zero_weight() { - WeightedChoice::new(&mut [Weighted { weight: 0, item: 0}, - Weighted { weight: 0, item: 1}]); - } - #[test] #[should_panic] - #[allow(deprecated)] - fn test_weighted_choice_weight_overflows() { - let x = ::core::u32::MAX / 2; // x + x + 2 is the overflow - WeightedChoice::new(&mut [Weighted { weight: x, item: 0 }, - Weighted { weight: 1, item: 1 }, - Weighted { weight: x, item: 2 }, - Weighted { weight: 1, item: 3 }]); - } - - #[cfg(feature="std")] - #[test] fn test_distributions_iter() { - use distributions::Normal; - let mut rng = ::test::rng(210); - let distr = Normal::new(10.0, 10.0); - let results: Vec<_> = distr.sample_iter(&mut rng).take(100).collect(); + use crate::distributions::Open01; + let mut rng = crate::test::rng(210); + let distr = Open01; + let results: Vec<f32> = distr.sample_iter(&mut rng).take(100).collect(); println!("{:?}", results); } + + #[test] + fn test_make_an_iter() { + fn ten_dice_rolls_other_than_five<'a, R: Rng>(rng: &'a mut R) -> impl Iterator<Item = i32> + 'a { + Uniform::new_inclusive(1, 6) + .sample_iter(rng) + .filter(|x| *x != 5) + .take(10) + } + + let mut rng = crate::test::rng(211); + let mut count = 0; + for val in ten_dice_rolls_other_than_five(&mut rng) { + assert!(val >= 1 && val <= 6 && val != 5); + count += 1; + } + assert_eq!(count, 10); + } } diff --git a/rand/src/distributions/normal.rs b/rand/src/distributions/normal.rs index b8d632e..7808baf 100644 --- a/rand/src/distributions/normal.rs +++ b/rand/src/distributions/normal.rs @@ -8,10 +8,11 @@ // except according to those terms. //! The normal and derived distributions. +#![allow(deprecated)] -use Rng; -use distributions::{ziggurat_tables, Distribution, Open01}; -use distributions::utils::ziggurat; +use crate::Rng; +use crate::distributions::{ziggurat_tables, Distribution, Open01}; +use crate::distributions::utils::ziggurat; /// Samples floating-point numbers according to the normal distribution /// `N(0, 1)` (a.k.a. a standard normal, or Gaussian). This is equivalent to @@ -25,15 +26,7 @@ use distributions::utils::ziggurat; /// Generate Normal Random Samples*]( /// https://www.doornik.com/research/ziggurat.pdf). /// Nuffield College, Oxford -/// -/// # Example -/// ``` -/// use rand::prelude::*; -/// use rand::distributions::StandardNormal; -/// -/// let val: f64 = SmallRng::from_entropy().sample(StandardNormal); -/// println!("{}", val); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct StandardNormal; @@ -80,18 +73,8 @@ impl Distribution<f64> for StandardNormal { /// Note that [`StandardNormal`] is an optimised implementation for mean 0, and /// standard deviation 1. /// -/// # Example -/// -/// ``` -/// use rand::distributions::{Normal, Distribution}; -/// -/// // mean 2, standard deviation 3 -/// let normal = Normal::new(2.0, 3.0); -/// let v = normal.sample(&mut rand::thread_rng()); -/// println!("{} is from a N(2, 9) distribution", v) -/// ``` -/// -/// [`StandardNormal`]: struct.StandardNormal.html +/// [`StandardNormal`]: crate::distributions::StandardNormal +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Normal { mean: f64, @@ -126,17 +109,7 @@ impl Distribution<f64> for Normal { /// /// If `X` is log-normal distributed, then `ln(X)` is `N(mean, std_dev**2)` /// distributed. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{LogNormal, Distribution}; -/// -/// // mean 2, standard deviation 3 -/// let log_normal = LogNormal::new(2.0, 3.0); -/// let v = log_normal.sample(&mut rand::thread_rng()); -/// println!("{} is from an ln N(2, 9) distribution", v) -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct LogNormal { norm: Normal @@ -163,13 +136,13 @@ impl Distribution<f64> for LogNormal { #[cfg(test)] mod tests { - use distributions::Distribution; + use crate::distributions::Distribution; use super::{Normal, LogNormal}; #[test] fn test_normal() { let norm = Normal::new(10.0, 10.0); - let mut rng = ::test::rng(210); + let mut rng = crate::test::rng(210); for _ in 0..1000 { norm.sample(&mut rng); } @@ -184,7 +157,7 @@ mod tests { #[test] fn test_log_normal() { let lnorm = LogNormal::new(10.0, 10.0); - let mut rng = ::test::rng(211); + let mut rng = crate::test::rng(211); for _ in 0..1000 { lnorm.sample(&mut rng); } diff --git a/rand/src/distributions/other.rs b/rand/src/distributions/other.rs index 2295f79..6ec0473 100644 --- a/rand/src/distributions/other.rs +++ b/rand/src/distributions/other.rs @@ -11,8 +11,8 @@ use core::char; use core::num::Wrapping; -use {Rng}; -use distributions::{Distribution, Standard, Uniform}; +use crate::Rng; +use crate::distributions::{Distribution, Standard, Uniform}; // ----- Sampling distributions ----- @@ -116,6 +116,7 @@ macro_rules! tuple_impl { } impl Distribution<()> for Standard { + #[allow(clippy::unused_unit)] #[inline] fn sample<R: Rng + ?Sized>(&self, _: &mut R) -> () { () } } @@ -176,13 +177,13 @@ impl<T> Distribution<Wrapping<T>> for Standard where Standard: Distribution<T> { #[cfg(test)] mod tests { - use {Rng, RngCore, Standard}; - use distributions::Alphanumeric; + use crate::{Rng, RngCore, Standard}; + use crate::distributions::Alphanumeric; #[cfg(all(not(feature="std"), feature="alloc"))] use alloc::string::String; #[test] fn test_misc() { - let rng: &mut RngCore = &mut ::test::rng(820); + let rng: &mut dyn RngCore = &mut crate::test::rng(820); rng.sample::<char, _>(Standard); rng.sample::<bool, _>(Standard); @@ -192,7 +193,7 @@ mod tests { #[test] fn test_chars() { use core::iter; - let mut rng = ::test::rng(805); + let mut rng = crate::test::rng(805); // Test by generating a relatively large number of chars, so we also // take the rejection sampling path. @@ -203,7 +204,7 @@ mod tests { #[test] fn test_alphanumeric() { - let mut rng = ::test::rng(806); + let mut rng = crate::test::rng(806); // Test by generating a relatively large number of chars, so we also // take the rejection sampling path. diff --git a/rand/src/distributions/pareto.rs b/rand/src/distributions/pareto.rs index 744a157..edc9122 100644 --- a/rand/src/distributions/pareto.rs +++ b/rand/src/distributions/pareto.rs @@ -7,20 +7,13 @@ // except according to those terms. //! The Pareto distribution. +#![allow(deprecated)] -use Rng; -use distributions::{Distribution, OpenClosed01}; +use crate::Rng; +use crate::distributions::{Distribution, OpenClosed01}; /// Samples floating-point numbers according to the Pareto distribution -/// -/// # Example -/// ``` -/// use rand::prelude::*; -/// use rand::distributions::Pareto; -/// -/// let val: f64 = SmallRng::from_entropy().sample(Pareto::new(1., 2.)); -/// println!("{}", val); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Pareto { scale: f64, @@ -51,7 +44,7 @@ impl Distribution<f64> for Pareto { #[cfg(test)] mod tests { - use distributions::Distribution; + use crate::distributions::Distribution; use super::Pareto; #[test] @@ -65,7 +58,7 @@ mod tests { let scale = 1.0; let shape = 2.0; let d = Pareto::new(scale, shape); - let mut rng = ::test::rng(1); + let mut rng = crate::test::rng(1); for _ in 0..1000 { let r = d.sample(&mut rng); assert!(r >= scale); diff --git a/rand/src/distributions/poisson.rs b/rand/src/distributions/poisson.rs index 1244caa..9fd6e99 100644 --- a/rand/src/distributions/poisson.rs +++ b/rand/src/distributions/poisson.rs @@ -8,25 +8,17 @@ // except according to those terms. //! The Poisson distribution. +#![allow(deprecated)] -use Rng; -use distributions::{Distribution, Cauchy}; -use distributions::utils::log_gamma; +use crate::Rng; +use crate::distributions::{Distribution, Cauchy}; +use crate::distributions::utils::log_gamma; /// The Poisson distribution `Poisson(lambda)`. /// /// This distribution has a density function: /// `f(k) = lambda^k * exp(-lambda) / k!` for `k >= 0`. -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{Poisson, Distribution}; -/// -/// let poi = Poisson::new(2.0); -/// let v = poi.sample(&mut rand::thread_rng()); -/// println!("{} is from a Poisson(2) distribution", v); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Poisson { lambda: f64, @@ -113,13 +105,14 @@ impl Distribution<u64> for Poisson { #[cfg(test)] mod test { - use distributions::Distribution; + use crate::distributions::Distribution; use super::Poisson; #[test] + #[cfg(not(miri))] // Miri is too slow fn test_poisson_10() { let poisson = Poisson::new(10.0); - let mut rng = ::test::rng(123); + let mut rng = crate::test::rng(123); let mut sum = 0; for _ in 0..1000 { sum += poisson.sample(&mut rng); @@ -130,10 +123,11 @@ mod test { } #[test] + #[cfg(not(miri))] // Miri doesn't support transcendental functions fn test_poisson_15() { // Take the 'high expected values' path let poisson = Poisson::new(15.0); - let mut rng = ::test::rng(123); + let mut rng = crate::test::rng(123); let mut sum = 0; for _ in 0..1000 { sum += poisson.sample(&mut rng); diff --git a/rand/src/distributions/triangular.rs b/rand/src/distributions/triangular.rs index a6eef5c..3e8f8b0 100644 --- a/rand/src/distributions/triangular.rs +++ b/rand/src/distributions/triangular.rs @@ -5,22 +5,15 @@ // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. + //! The triangular distribution. +#![allow(deprecated)] -use Rng; -use distributions::{Distribution, Standard}; +use crate::Rng; +use crate::distributions::{Distribution, Standard}; /// The triangular distribution. -/// -/// # Example -/// -/// ```rust -/// use rand::distributions::{Triangular, Distribution}; -/// -/// let d = Triangular::new(0., 5., 2.5); -/// let v = d.sample(&mut rand::thread_rng()); -/// println!("{} is from a triangular distribution", v); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Triangular { min: f64, @@ -61,7 +54,7 @@ impl Distribution<f64> for Triangular { #[cfg(test)] mod test { - use distributions::Distribution; + use crate::distributions::Distribution; use super::Triangular; #[test] @@ -78,7 +71,7 @@ mod test { #[test] fn test_sample() { let norm = Triangular::new(0., 1., 0.5); - let mut rng = ::test::rng(1); + let mut rng = crate::test::rng(1); for _ in 0..1000 { norm.sample(&mut rng); } diff --git a/rand/src/distributions/uniform.rs b/rand/src/distributions/uniform.rs index ceed77d..8c90f4e 100644 --- a/rand/src/distributions/uniform.rs +++ b/rand/src/distributions/uniform.rs @@ -15,13 +15,13 @@ //! [`Uniform`]. //! //! This distribution is provided with support for several primitive types -//! (all integer and floating-point types) as well as `std::time::Duration`, +//! (all integer and floating-point types) as well as [`std::time::Duration`], //! and supports extension to user-defined types via a type-specific *back-end* //! implementation. //! //! The types [`UniformInt`], [`UniformFloat`] and [`UniformDuration`] are the //! back-ends supporting sampling from primitive integer and floating-point -//! ranges as well as from `std::time::Duration`; these types do not normally +//! ranges as well as from [`std::time::Duration`]; these types do not normally //! need to be used directly (unless implementing a derived back-end). //! //! # Example usage @@ -100,28 +100,26 @@ //! let x = uniform.sample(&mut thread_rng()); //! ``` //! -//! [`Uniform`]: struct.Uniform.html -//! [`Rng::gen_range`]: ../../trait.Rng.html#method.gen_range -//! [`SampleUniform`]: trait.SampleUniform.html -//! [`UniformSampler`]: trait.UniformSampler.html -//! [`UniformInt`]: struct.UniformInt.html -//! [`UniformFloat`]: struct.UniformFloat.html -//! [`UniformDuration`]: struct.UniformDuration.html -//! [`SampleBorrow::borrow`]: trait.SampleBorrow.html#method.borrow +//! [`SampleUniform`]: crate::distributions::uniform::SampleUniform +//! [`UniformSampler`]: crate::distributions::uniform::UniformSampler +//! [`UniformInt`]: crate::distributions::uniform::UniformInt +//! [`UniformFloat`]: crate::distributions::uniform::UniformFloat +//! [`UniformDuration`]: crate::distributions::uniform::UniformDuration +//! [`SampleBorrow::borrow`]: crate::distributions::uniform::SampleBorrow::borrow #[cfg(feature = "std")] use std::time::Duration; -#[cfg(all(not(feature = "std"), rustc_1_25))] +#[cfg(not(feature = "std"))] use core::time::Duration; -use Rng; -use distributions::Distribution; -use distributions::float::IntoFloat; -use distributions::utils::{WideningMultiply, FloatSIMDUtils, FloatAsSIMD, BoolAsSIMD}; +use crate::Rng; +use crate::distributions::Distribution; +use crate::distributions::float::IntoFloat; +use crate::distributions::utils::{WideningMultiply, FloatSIMDUtils, FloatAsSIMD, BoolAsSIMD}; #[cfg(not(feature = "std"))] #[allow(unused_imports)] // rustc doesn't detect that this is actually used -use distributions::utils::Float; +use crate::distributions::utils::Float; #[cfg(feature="simd_support")] @@ -165,10 +163,8 @@ use packed_simd::*; /// } /// ``` /// -/// [`Uniform::new`]: struct.Uniform.html#method.new -/// [`Uniform::new_inclusive`]: struct.Uniform.html#method.new_inclusive -/// [`new`]: struct.Uniform.html#method.new -/// [`new_inclusive`]: struct.Uniform.html#method.new_inclusive +/// [`new`]: Uniform::new +/// [`new_inclusive`]: Uniform::new_inclusive #[derive(Clone, Copy, Debug)] pub struct Uniform<X: SampleUniform> { inner: X::Sampler, @@ -206,9 +202,7 @@ impl<X: SampleUniform> Distribution<X> for Uniform<X> { /// See the [module documentation] on how to implement [`Uniform`] range /// sampling for a custom type. /// -/// [`UniformSampler`]: trait.UniformSampler.html -/// [module documentation]: index.html -/// [`Uniform`]: struct.Uniform.html +/// [module documentation]: crate::distributions::uniform pub trait SampleUniform: Sized { /// The `UniformSampler` implementation supporting type `X`. type Sampler: UniformSampler<X = Self>; @@ -222,9 +216,8 @@ pub trait SampleUniform: Sized { /// Implementation of [`sample_single`] is optional, and is only useful when /// the implementation can be faster than `Self::new(low, high).sample(rng)`. /// -/// [module documentation]: index.html -/// [`Uniform`]: struct.Uniform.html -/// [`sample_single`]: trait.UniformSampler.html#method.sample_single +/// [module documentation]: crate::distributions::uniform +/// [`sample_single`]: UniformSampler::sample_single pub trait UniformSampler: Sized { /// The type sampled by this implementation. type X; @@ -253,14 +246,11 @@ pub trait UniformSampler: Sized { /// Sample a single value uniformly from a range with inclusive lower bound /// and exclusive upper bound `[low, high)`. /// - /// Usually users should not call this directly but instead use - /// `Uniform::sample_single`, which asserts that `low < high` before calling - /// this. - /// - /// Via this method, implementations can provide a method optimized for - /// sampling only a single value from the specified range. The default - /// implementation simply calls `UniformSampler::new` then `sample` on the - /// result. + /// By default this is implemented using + /// `UniformSampler::new(low, high).sample(rng)`. However, for some types + /// more optimal implementations for single usage may be provided via this + /// method (which is the case for integers and floats). + /// Results may not be identical. fn sample_single<R: Rng + ?Sized, B1, B2>(low: B1, high: B2, rng: &mut R) -> Self::X where B1: SampleBorrow<Self::X> + Sized, @@ -277,7 +267,6 @@ impl<X: SampleUniform> From<::core::ops::Range<X>> for Uniform<X> { } } -#[cfg(rustc_1_27)] impl<X: SampleUniform> From<::core::ops::RangeInclusive<X>> for Uniform<X> { fn from(r: ::core::ops::RangeInclusive<X>) -> Uniform<X> { Uniform::new_inclusive(r.start(), r.end()) @@ -288,11 +277,11 @@ impl<X: SampleUniform> From<::core::ops::RangeInclusive<X>> for Uniform<X> { /// only for SampleUniform and references to SampleUniform in /// order to resolve ambiguity issues. /// -/// [`Borrow`]: https://doc.rust-lang.org/std/borrow/trait.Borrow.html +/// [`Borrow`]: std::borrow::Borrow pub trait SampleBorrow<Borrowed> { /// Immutably borrows from an owned value. See [`Borrow::borrow`] /// - /// [`Borrow::borrow`]: https://doc.rust-lang.org/std/borrow/trait.Borrow.html#tymethod.borrow + /// [`Borrow::borrow`]: std::borrow::Borrow::borrow fn borrow(&self) -> &Borrowed; } impl<Borrowed> SampleBorrow<Borrowed> for Borrowed where Borrowed: SampleUniform { @@ -316,48 +305,42 @@ impl<'a, Borrowed> SampleBorrow<Borrowed> for &'a Borrowed where Borrowed: Sampl /// /// # Implementation notes /// +/// For simplicity, we use the same generic struct `UniformInt<X>` for all +/// integer types `X`. This gives us only one field type, `X`; to store unsigned +/// values of this size, we take use the fact that these conversions are no-ops. +/// /// For a closed range, the number of possible numbers we should generate is -/// `range = (high - low + 1)`. It is not possible to end up with a uniform -/// distribution if we map *all* the random integers that can be generated to -/// this range. We have to map integers from a `zone` that is a multiple of the -/// range. The rest of the integers, that cause a bias, are rejected. +/// `range = (high - low + 1)`. To avoid bias, we must ensure that the size of +/// our sample space, `zone`, is a multiple of `range`; other values must be +/// rejected (by replacing with a new random sample). /// -/// The problem with `range` is that to cover the full range of the type, it has -/// to store `unsigned_max + 1`, which can't be represented. But if the range -/// covers the full range of the type, no modulus is needed. A range of size 0 -/// can't exist, so we use that to represent this special case. Wrapping -/// arithmetic even makes representing `unsigned_max + 1` as 0 simple. +/// As a special case, we use `range = 0` to represent the full range of the +/// result type (i.e. for `new_inclusive($ty::MIN, $ty::MAX)`). /// -/// We don't calculate `zone` directly, but first calculate the number of -/// integers to reject. To handle `unsigned_max + 1` not fitting in the type, -/// we use: -/// `ints_to_reject = (unsigned_max + 1) % range;` -/// `ints_to_reject = (unsigned_max - range + 1) % range;` +/// The optimum `zone` is the largest product of `range` which fits in our +/// (unsigned) target type. We calculate this by calculating how many numbers we +/// must reject: `reject = (MAX + 1) % range = (MAX - range + 1) % range`. Any (large) +/// product of `range` will suffice, thus in `sample_single` we multiply by a +/// power of 2 via bit-shifting (faster but may cause more rejections). /// -/// The smallest integer PRNGs generate is `u32`. That is why for small integer -/// sizes (`i8`/`u8` and `i16`/`u16`) there is an optimization: don't pick the -/// largest zone that can fit in the small type, but pick the largest zone that -/// can fit in an `u32`. `ints_to_reject` is always less than half the size of -/// the small integer. This means the first bit of `zone` is always 1, and so -/// are all the other preceding bits of a larger integer. The easiest way to -/// grow the `zone` for the larger type is to simply sign extend it. +/// The smallest integer PRNGs generate is `u32`. For 8- and 16-bit outputs we +/// use `u32` for our `zone` and samples (because it's not slower and because +/// it reduces the chance of having to reject a sample). In this case we cannot +/// store `zone` in the target type since it is too large, however we know +/// `ints_to_reject < range <= $unsigned::MAX`. /// /// An alternative to using a modulus is widening multiply: After a widening /// multiply by `range`, the result is in the high word. Then comparing the low /// word against `zone` makes sure our distribution is uniform. -/// -/// [`UniformSampler`]: trait.UniformSampler.html -/// [`Uniform`]: struct.Uniform.html #[derive(Clone, Copy, Debug)] pub struct UniformInt<X> { low: X, range: X, - zone: X, + z: X, // either ints_to_reject or zone depending on implementation } macro_rules! uniform_int_impl { - ($ty:ty, $signed:ty, $unsigned:ident, - $i_large:ident, $u_large:ident) => { + ($ty:ty, $unsigned:ident, $u_large:ident) => { impl SampleUniform for $ty { type Sampler = UniformInt<$ty>; } @@ -392,34 +375,30 @@ macro_rules! uniform_int_impl { let high = *high_b.borrow(); assert!(low <= high, "Uniform::new_inclusive called with `low > high`"); - let unsigned_max = ::core::$unsigned::MAX; + let unsigned_max = ::core::$u_large::MAX; let range = high.wrapping_sub(low).wrapping_add(1) as $unsigned; let ints_to_reject = if range > 0 { + let range = $u_large::from(range); (unsigned_max - range + 1) % range } else { 0 }; - let zone = unsigned_max - ints_to_reject; UniformInt { low: low, // These are really $unsigned values, but store as $ty: range: range as $ty, - zone: zone as $ty + z: ints_to_reject as $unsigned as $ty } } fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X { let range = self.range as $unsigned as $u_large; if range > 0 { - // Grow `zone` to fit a type of at least 32 bits, by - // sign-extending it (the first bit is always 1, so are all - // the preceding bits of the larger type). - // For types that already have the right size, all the - // casting is a no-op. - let zone = self.zone as $signed as $i_large as $u_large; + let unsigned_max = ::core::$u_large::MAX; + let zone = unsigned_max - (self.z as $unsigned as $u_large); loop { let v: $u_large = rng.gen(); let (hi, lo) = v.wmul(range); @@ -441,7 +420,7 @@ macro_rules! uniform_int_impl { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low < high, - "Uniform::sample_single called with low >= high"); + "UniformSampler::sample_single: low >= high"); let range = high.wrapping_sub(low) as $unsigned as $u_large; let zone = if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned { @@ -469,20 +448,20 @@ macro_rules! uniform_int_impl { } } -uniform_int_impl! { i8, i8, u8, i32, u32 } -uniform_int_impl! { i16, i16, u16, i32, u32 } -uniform_int_impl! { i32, i32, u32, i32, u32 } -uniform_int_impl! { i64, i64, u64, i64, u64 } -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] -uniform_int_impl! { i128, i128, u128, u128, u128 } -uniform_int_impl! { isize, isize, usize, isize, usize } -uniform_int_impl! { u8, i8, u8, i32, u32 } -uniform_int_impl! { u16, i16, u16, i32, u32 } -uniform_int_impl! { u32, i32, u32, i32, u32 } -uniform_int_impl! { u64, i64, u64, i64, u64 } -uniform_int_impl! { usize, isize, usize, isize, usize } -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] -uniform_int_impl! { u128, u128, u128, i128, u128 } +uniform_int_impl! { i8, u8, u32 } +uniform_int_impl! { i16, u16, u32 } +uniform_int_impl! { i32, u32, u32 } +uniform_int_impl! { i64, u64, u64 } +#[cfg(not(target_os = "emscripten"))] +uniform_int_impl! { i128, u128, u128 } +uniform_int_impl! { isize, usize, usize } +uniform_int_impl! { u8, u8, u32 } +uniform_int_impl! { u16, u16, u32 } +uniform_int_impl! { u32, u32, u32 } +uniform_int_impl! { u64, u64, u64 } +uniform_int_impl! { usize, usize, usize } +#[cfg(not(target_os = "emscripten"))] +uniform_int_impl! { u128, u128, u128 } #[cfg(all(feature = "simd_support", feature = "nightly"))] macro_rules! uniform_simd_int_impl { @@ -544,13 +523,13 @@ macro_rules! uniform_simd_int_impl { low: low, // These are really $unsigned values, but store as $ty: range: range.cast(), - zone: zone.cast(), + z: zone.cast(), } } fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X { let range: $unsigned = self.range.cast(); - let zone: $unsigned = self.zone.cast(); + let zone: $unsigned = self.z.cast(); // This might seem very slow, generating a whole new // SIMD vector for every sample rejection. For most uses @@ -646,11 +625,9 @@ uniform_simd_int_impl! { /// multiply and addition. Values produced this way have what equals 22 bits of /// random digits for an `f32`, and 52 for an `f64`. /// -/// [`UniformSampler`]: trait.UniformSampler.html -/// [`new`]: trait.UniformSampler.html#tymethod.new -/// [`new_inclusive`]: trait.UniformSampler.html#tymethod.new_inclusive -/// [`Uniform`]: struct.Uniform.html -/// [`Standard`]: ../struct.Standard.html +/// [`new`]: UniformSampler::new +/// [`new_inclusive`]: UniformSampler::new_inclusive +/// [`Standard`]: crate::distributions::Standard #[derive(Clone, Copy, Debug)] pub struct UniformFloat<X> { low: X, @@ -748,7 +725,7 @@ macro_rules! uniform_float_impl { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low.all_lt(high), - "Uniform::sample_single called with low >= high"); + "UniformSampler::sample_single: low >= high"); let mut scale = high - low; loop { @@ -799,7 +776,7 @@ macro_rules! uniform_float_impl { let mask = !scale.finite_mask(); if mask.any() { assert!(low.all_finite() && high.all_finite(), - "Uniform::sample_single called with non-finite boundaries"); + "Uniform::sample_single: low and high must be finite"); scale = scale.decrease_masked(mask); } } @@ -833,17 +810,12 @@ uniform_float_impl! { f64x8, u64x8, f64, u64, 64 - 52 } /// /// Unless you are implementing [`UniformSampler`] for your own types, this type /// should not be used directly, use [`Uniform`] instead. -/// -/// [`UniformSampler`]: trait.UniformSampler.html -/// [`Uniform`]: struct.Uniform.html -#[cfg(any(feature = "std", rustc_1_25))] #[derive(Clone, Copy, Debug)] pub struct UniformDuration { mode: UniformDurationMode, offset: u32, } -#[cfg(any(feature = "std", rustc_1_25))] #[derive(Debug, Copy, Clone)] enum UniformDurationMode { Small { @@ -860,12 +832,10 @@ enum UniformDurationMode { } } -#[cfg(any(feature = "std", rustc_1_25))] impl SampleUniform for Duration { type Sampler = UniformDuration; } -#[cfg(any(feature = "std", rustc_1_25))] impl UniformSampler for UniformDuration { type X = Duration; @@ -895,8 +865,8 @@ impl UniformSampler for UniformDuration { let mut high_n = high.subsec_nanos(); if high_n < low_n { - high_s = high_s - 1; - high_n = high_n + 1_000_000_000; + high_s -= 1; + high_n += 1_000_000_000; } let mode = if low_s == high_s { @@ -907,10 +877,10 @@ impl UniformSampler for UniformDuration { } else { let max = high_s .checked_mul(1_000_000_000) - .and_then(|n| n.checked_add(high_n as u64)); + .and_then(|n| n.checked_add(u64::from(high_n))); if let Some(higher_bound) = max { - let lower_bound = low_s * 1_000_000_000 + low_n as u64; + let lower_bound = low_s * 1_000_000_000 + u64::from(low_n); UniformDurationMode::Medium { nanos: Uniform::new_inclusive(lower_bound, higher_bound), } @@ -959,10 +929,10 @@ impl UniformSampler for UniformDuration { #[cfg(test)] mod tests { - use Rng; - use rngs::mock::StepRng; - use distributions::uniform::Uniform; - use distributions::utils::FloatAsSIMD; + use crate::Rng; + use crate::rngs::mock::StepRng; + use crate::distributions::uniform::Uniform; + use crate::distributions::utils::FloatAsSIMD; #[cfg(feature="simd_support")] use packed_simd::*; #[should_panic] @@ -973,7 +943,7 @@ mod tests { #[test] fn test_uniform_good_limits_equal_int() { - let mut rng = ::test::rng(804); + let mut rng = crate::test::rng(804); let dist = Uniform::new_inclusive(10, 10); for _ in 0..20 { assert_eq!(rng.sample(dist), 10); @@ -987,13 +957,14 @@ mod tests { } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_integers() { use core::{i8, i16, i32, i64, isize}; use core::{u8, u16, u32, u64, usize}; - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] + #[cfg(not(target_os = "emscripten"))] use core::{i128, u128}; - let mut rng = ::test::rng(251); + let mut rng = crate::test::rng(251); macro_rules! t { ($ty:ident, $v:expr, $le:expr, $lt:expr) => {{ for &(low, high) in $v.iter() { @@ -1054,7 +1025,7 @@ mod tests { } t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] + #[cfg(not(target_os = "emscripten"))] t!(i128, u128); #[cfg(all(feature = "simd_support", feature = "nightly"))] @@ -1071,8 +1042,9 @@ mod tests { } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_floats() { - let mut rng = ::test::rng(252); + let mut rng = crate::test::rng(252); let mut zero_rng = StepRng::new(0, 0); let mut max_rng = StepRng::new(0xffff_ffff_ffff_ffff, 0); macro_rules! t { @@ -1155,11 +1127,12 @@ mod tests { #[cfg(all(feature="std", not(target_arch = "wasm32"), not(target_arch = "asmjs")))] + #[cfg(not(miri))] // Miri does not support catching panics fn test_float_assertions() { use std::panic::catch_unwind; use super::SampleUniform; fn range<T: SampleUniform>(low: T, high: T) { - let mut rng = ::test::rng(253); + let mut rng = crate::test::rng(253); rng.gen_range(low, high); } @@ -1209,14 +1182,14 @@ mod tests { #[test] - #[cfg(any(feature = "std", rustc_1_25))] + #[cfg(not(miri))] // Miri is too slow fn test_durations() { #[cfg(feature = "std")] use std::time::Duration; - #[cfg(all(not(feature = "std"), rustc_1_25))] + #[cfg(not(feature = "std"))] use core::time::Duration; - let mut rng = ::test::rng(253); + let mut rng = crate::test::rng(253); let v = &[(Duration::new(10, 50000), Duration::new(100, 1234)), (Duration::new(0, 100), Duration::new(1, 50)), @@ -1232,7 +1205,7 @@ mod tests { #[test] fn test_custom_uniform() { - use distributions::uniform::{UniformSampler, UniformFloat, SampleUniform, SampleBorrow}; + use crate::distributions::uniform::{UniformSampler, UniformFloat, SampleUniform, SampleBorrow}; #[derive(Clone, Copy, PartialEq, PartialOrd)] struct MyF32 { x: f32, @@ -1267,7 +1240,7 @@ mod tests { let (low, high) = (MyF32{ x: 17.0f32 }, MyF32{ x: 22.0f32 }); let uniform = Uniform::new(low, high); - let mut rng = ::test::rng(804); + let mut rng = crate::test::rng(804); for _ in 0..100 { let x: MyF32 = rng.sample(uniform); assert!(low <= x && x < high); @@ -1284,7 +1257,6 @@ mod tests { assert_eq!(r.inner.scale, 5.0); } - #[cfg(rustc_1_27)] #[test] fn test_uniform_from_std_range_inclusive() { let r = Uniform::from(2u32..=6); diff --git a/rand/src/distributions/unit_circle.rs b/rand/src/distributions/unit_circle.rs index 01ab76a..56e75b6 100644 --- a/rand/src/distributions/unit_circle.rs +++ b/rand/src/distributions/unit_circle.rs @@ -6,28 +6,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use Rng; -use distributions::{Distribution, Uniform}; +#![allow(deprecated)] +#![allow(clippy::all)] + +use crate::Rng; +use crate::distributions::{Distribution, Uniform}; /// Samples uniformly from the edge of the unit circle in two dimensions. /// /// Implemented via a method by von Neumann[^1]. /// -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{UnitCircle, Distribution}; -/// -/// let circle = UnitCircle::new(); -/// let v = circle.sample(&mut rand::thread_rng()); -/// println!("{:?} is from the unit circle.", v) -/// ``` -/// /// [^1]: von Neumann, J. (1951) [*Various Techniques Used in Connection with /// Random Digits.*](https://mcnp.lanl.gov/pdf_files/nbs_vonneumann.pdf) /// NBS Appl. Math. Ser., No. 12. Washington, DC: U.S. Government Printing /// Office, pp. 36-38. +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct UnitCircle; @@ -61,7 +54,7 @@ impl Distribution<[f64; 2]> for UnitCircle { #[cfg(test)] mod tests { - use distributions::Distribution; + use crate::distributions::Distribution; use super::UnitCircle; /// Assert that two numbers are almost equal to each other. @@ -82,7 +75,7 @@ mod tests { #[test] fn norm() { - let mut rng = ::test::rng(1); + let mut rng = crate::test::rng(1); let dist = UnitCircle::new(); for _ in 0..1000 { let x = dist.sample(&mut rng); @@ -92,10 +85,17 @@ mod tests { #[test] fn value_stability() { - let mut rng = ::test::rng(2); - let dist = UnitCircle::new(); - assert_eq!(dist.sample(&mut rng), [-0.8032118336637037, 0.5956935036263119]); - assert_eq!(dist.sample(&mut rng), [-0.4742919588505423, -0.880367615130018]); - assert_eq!(dist.sample(&mut rng), [0.9297328981467168, 0.368234623716601]); + let mut rng = crate::test::rng(2); + let expected = [ + [-0.9965658683520504, -0.08280380447614634], + [-0.9790853270389644, -0.20345004884984505], + [-0.8449189758898707, 0.5348943112253227], + ]; + let samples = [ + UnitCircle.sample(&mut rng), + UnitCircle.sample(&mut rng), + UnitCircle.sample(&mut rng), + ]; + assert_eq!(samples, expected); } } diff --git a/rand/src/distributions/unit_sphere.rs b/rand/src/distributions/unit_sphere.rs index 37de88b..188f48c 100644 --- a/rand/src/distributions/unit_sphere.rs +++ b/rand/src/distributions/unit_sphere.rs @@ -6,27 +6,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use Rng; -use distributions::{Distribution, Uniform}; +#![allow(deprecated)] +#![allow(clippy::all)] + +use crate::Rng; +use crate::distributions::{Distribution, Uniform}; /// Samples uniformly from the surface of the unit sphere in three dimensions. /// /// Implemented via a method by Marsaglia[^1]. /// -/// -/// # Example -/// -/// ``` -/// use rand::distributions::{UnitSphereSurface, Distribution}; -/// -/// let sphere = UnitSphereSurface::new(); -/// let v = sphere.sample(&mut rand::thread_rng()); -/// println!("{:?} is from the unit sphere surface.", v) -/// ``` -/// /// [^1]: Marsaglia, George (1972). [*Choosing a Point from the Surface of a /// Sphere.*](https://doi.org/10.1214/aoms/1177692644) /// Ann. Math. Statist. 43, no. 2, 645--646. +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct UnitSphereSurface; @@ -56,7 +49,7 @@ impl Distribution<[f64; 3]> for UnitSphereSurface { #[cfg(test)] mod tests { - use distributions::Distribution; + use crate::distributions::Distribution; use super::UnitSphereSurface; /// Assert that two numbers are almost equal to each other. @@ -77,7 +70,7 @@ mod tests { #[test] fn norm() { - let mut rng = ::test::rng(1); + let mut rng = crate::test::rng(1); let dist = UnitSphereSurface::new(); for _ in 0..1000 { let x = dist.sample(&mut rng); @@ -87,13 +80,17 @@ mod tests { #[test] fn value_stability() { - let mut rng = ::test::rng(2); - let dist = UnitSphereSurface::new(); - assert_eq!(dist.sample(&mut rng), - [-0.24950027180862533, -0.7552572587896719, 0.6060825747478084]); - assert_eq!(dist.sample(&mut rng), - [0.47604534507233487, -0.797200864987207, -0.3712837328763685]); - assert_eq!(dist.sample(&mut rng), - [0.9795722330927367, 0.18692349236651176, 0.07414747571708524]); + let mut rng = crate::test::rng(2); + let expected = [ + [0.03247542860231647, -0.7830477442152738, 0.6211131755296027], + [-0.09978440840914075, 0.9706650829833128, -0.21875184231323952], + [0.2735582468624679, 0.9435374242279655, -0.1868234852870203], + ]; + let samples = [ + UnitSphereSurface.sample(&mut rng), + UnitSphereSurface.sample(&mut rng), + UnitSphereSurface.sample(&mut rng), + ]; + assert_eq!(samples, expected); } } diff --git a/rand/src/distributions/utils.rs b/rand/src/distributions/utils.rs index d4d3642..3af4e86 100644 --- a/rand/src/distributions/utils.rs +++ b/rand/src/distributions/utils.rs @@ -11,9 +11,9 @@ #[cfg(feature="simd_support")] use packed_simd::*; #[cfg(feature="std")] -use distributions::ziggurat_tables; +use crate::distributions::ziggurat_tables; #[cfg(feature="std")] -use Rng; +use crate::Rng; pub trait WideningMultiply<RHS = Self> { @@ -61,7 +61,7 @@ macro_rules! wmul_impl { wmul_impl! { u8, u16, 8 } wmul_impl! { u16, u32, 16 } wmul_impl! { u32, u64, 32 } -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] +#[cfg(not(target_os = "emscripten"))] wmul_impl! { u64, u128, 64 } // This code is a translation of the __mulddi3 function in LLVM's @@ -125,9 +125,9 @@ macro_rules! wmul_impl_large { )+ }; } -#[cfg(not(all(rustc_1_26, not(target_os = "emscripten"))))] +#[cfg(target_os = "emscripten")] wmul_impl_large! { u64, 32 } -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] +#[cfg(not(target_os = "emscripten"))] wmul_impl_large! { u128, 64 } macro_rules! wmul_impl_usize { @@ -249,13 +249,9 @@ pub(crate) trait FloatSIMDUtils { /// Implement functions available in std builds but missing from core primitives #[cfg(not(std))] pub(crate) trait Float : Sized { - type Bits; - fn is_nan(self) -> bool; fn is_infinite(self) -> bool; fn is_finite(self) -> bool; - fn to_bits(self) -> Self::Bits; - fn from_bits(v: Self::Bits) -> Self; } /// Implement functions on f32/f64 to give them APIs similar to SIMD types @@ -289,8 +285,6 @@ macro_rules! scalar_float_impl { ($ty:ident, $uty:ident) => { #[cfg(not(std))] impl Float for $ty { - type Bits = $uty; - #[inline] fn is_nan(self) -> bool { self != self @@ -305,17 +299,6 @@ macro_rules! scalar_float_impl { fn is_finite(self) -> bool { !(self.is_nan() || self.is_infinite()) } - - #[inline] - fn to_bits(self) -> Self::Bits { - unsafe { ::core::mem::transmute(self) } - } - - #[inline] - fn from_bits(v: Self::Bits) -> Self { - // It turns out the safety issues with sNaN were overblown! Hooray! - unsafe { ::core::mem::transmute(v) } - } } impl FloatSIMDUtils for $ty { @@ -383,6 +366,7 @@ macro_rules! simd_impl { <$ty>::from_bits(<$uty>::from_bits(self) + <$uty>::from_bits(mask)) } type UInt = $uty; + #[inline] fn cast_from_int(i: Self::UInt) -> Self { i.cast() } } } @@ -464,7 +448,7 @@ pub fn ziggurat<R: Rng + ?Sized, P, Z>( mut pdf: P, mut zero_case: Z) -> f64 where P: FnMut(f64) -> f64, Z: FnMut(&mut R, f64) -> f64 { - use distributions::float::IntoFloat; + use crate::distributions::float::IntoFloat; loop { // As an optimisation we re-implement the conversion to a f64. // From the remaining 12 most significant bits we use 8 to construct `i`. diff --git a/rand/src/distributions/weibull.rs b/rand/src/distributions/weibull.rs index 5fbe10a..483714f 100644 --- a/rand/src/distributions/weibull.rs +++ b/rand/src/distributions/weibull.rs @@ -7,20 +7,13 @@ // except according to those terms. //! The Weibull distribution. +#![allow(deprecated)] -use Rng; -use distributions::{Distribution, OpenClosed01}; +use crate::Rng; +use crate::distributions::{Distribution, OpenClosed01}; /// Samples floating-point numbers according to the Weibull distribution -/// -/// # Example -/// ``` -/// use rand::prelude::*; -/// use rand::distributions::Weibull; -/// -/// let val: f64 = SmallRng::from_entropy().sample(Weibull::new(1., 10.)); -/// println!("{}", val); -/// ``` +#[deprecated(since="0.7.0", note="moved to rand_distr crate")] #[derive(Clone, Copy, Debug)] pub struct Weibull { inv_shape: f64, @@ -48,7 +41,7 @@ impl Distribution<f64> for Weibull { #[cfg(test)] mod tests { - use distributions::Distribution; + use crate::distributions::Distribution; use super::Weibull; #[test] @@ -62,7 +55,7 @@ mod tests { let scale = 1.0; let shape = 2.0; let d = Weibull::new(scale, shape); - let mut rng = ::test::rng(1); + let mut rng = crate::test::rng(1); for _ in 0..1000 { let r = d.sample(&mut rng); assert!(r >= 0.); diff --git a/rand/src/distributions/weighted/alias_method.rs b/rand/src/distributions/weighted/alias_method.rs new file mode 100644 index 0000000..bdd4ba0 --- /dev/null +++ b/rand/src/distributions/weighted/alias_method.rs @@ -0,0 +1,499 @@ +//! This module contains an implementation of alias method for sampling random +//! indices with probabilities proportional to a collection of weights. + +use super::WeightedError; +#[cfg(not(feature = "std"))] +use crate::alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use crate::alloc::vec; +use core::fmt; +use core::iter::Sum; +use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; +use crate::distributions::uniform::SampleUniform; +use crate::distributions::Distribution; +use crate::distributions::Uniform; +use crate::Rng; + +/// A distribution using weighted sampling to pick a discretely selected item. +/// +/// Sampling a [`WeightedIndex<W>`] distribution returns the index of a randomly +/// selected element from the vector used to create the [`WeightedIndex<W>`]. +/// The chance of a given element being picked is proportional to the value of +/// the element. The weights can have any type `W` for which a implementation of +/// [`Weight`] exists. +/// +/// # Performance +/// +/// Given that `n` is the number of items in the vector used to create an +/// [`WeightedIndex<W>`], [`WeightedIndex<W>`] will require `O(n)` amount of +/// memory. More specifically it takes up some constant amount of memory plus +/// the vector used to create it and a [`Vec<u32>`] with capacity `n`. +/// +/// Time complexity for the creation of a [`WeightedIndex<W>`] is `O(n)`. +/// Sampling is `O(1)`, it makes a call to [`Uniform<u32>::sample`] and a call +/// to [`Uniform<W>::sample`]. +/// +/// # Example +/// +/// ``` +/// use rand::distributions::weighted::alias_method::WeightedIndex; +/// use rand::prelude::*; +/// +/// let choices = vec!['a', 'b', 'c']; +/// let weights = vec![2, 1, 1]; +/// let dist = WeightedIndex::new(weights).unwrap(); +/// let mut rng = thread_rng(); +/// for _ in 0..100 { +/// // 50% chance to print 'a', 25% chance to print 'b', 25% chance to print 'c' +/// println!("{}", choices[dist.sample(&mut rng)]); +/// } +/// +/// let items = [('a', 0), ('b', 3), ('c', 7)]; +/// let dist2 = WeightedIndex::new(items.iter().map(|item| item.1).collect()).unwrap(); +/// for _ in 0..100 { +/// // 0% chance to print 'a', 30% chance to print 'b', 70% chance to print 'c' +/// println!("{}", items[dist2.sample(&mut rng)].0); +/// } +/// ``` +/// +/// [`WeightedIndex<W>`]: crate::distributions::weighted::alias_method::WeightedIndex +/// [`Weight`]: crate::distributions::weighted::alias_method::Weight +/// [`Vec<u32>`]: Vec +/// [`Uniform<u32>::sample`]: Distribution::sample +/// [`Uniform<W>::sample`]: Distribution::sample +pub struct WeightedIndex<W: Weight> { + aliases: Vec<u32>, + no_alias_odds: Vec<W>, + uniform_index: Uniform<u32>, + uniform_within_weight_sum: Uniform<W>, +} + +impl<W: Weight> WeightedIndex<W> { + /// Creates a new [`WeightedIndex`]. + /// + /// Returns an error if: + /// - The vector is empty. + /// - The vector is longer than `u32::MAX`. + /// - For any weight `w`: `w < 0` or `w > max` where `max = W::MAX / + /// weights.len()`. + /// - The sum of weights is zero. + pub fn new(weights: Vec<W>) -> Result<Self, WeightedError> { + let n = weights.len(); + if n == 0 { + return Err(WeightedError::NoItem); + } else if n > ::core::u32::MAX as usize { + return Err(WeightedError::TooMany); + } + let n = n as u32; + + let max_weight_size = W::try_from_u32_lossy(n) + .map(|n| W::MAX / n) + .unwrap_or(W::ZERO); + if !weights + .iter() + .all(|&w| W::ZERO <= w && w <= max_weight_size) + { + return Err(WeightedError::InvalidWeight); + } + + // The sum of weights will represent 100% of no alias odds. + let weight_sum = Weight::sum(weights.as_slice()); + // Prevent floating point overflow due to rounding errors. + let weight_sum = if weight_sum > W::MAX { + W::MAX + } else { + weight_sum + }; + if weight_sum == W::ZERO { + return Err(WeightedError::AllWeightsZero); + } + + // `weight_sum` would have been zero if `try_from_lossy` causes an error here. + let n_converted = W::try_from_u32_lossy(n).unwrap(); + + let mut no_alias_odds = weights; + for odds in no_alias_odds.iter_mut() { + *odds *= n_converted; + // Prevent floating point overflow due to rounding errors. + *odds = if *odds > W::MAX { W::MAX } else { *odds }; + } + + /// This struct is designed to contain three data structures at once, + /// sharing the same memory. More precisely it contains two linked lists + /// and an alias map, which will be the output of this method. To keep + /// the three data structures from getting in each other's way, it must + /// be ensured that a single index is only ever in one of them at the + /// same time. + struct Aliases { + aliases: Vec<u32>, + smalls_head: u32, + bigs_head: u32, + } + + impl Aliases { + fn new(size: u32) -> Self { + Aliases { + aliases: vec![0; size as usize], + smalls_head: ::core::u32::MAX, + bigs_head: ::core::u32::MAX, + } + } + + fn push_small(&mut self, idx: u32) { + self.aliases[idx as usize] = self.smalls_head; + self.smalls_head = idx; + } + + fn push_big(&mut self, idx: u32) { + self.aliases[idx as usize] = self.bigs_head; + self.bigs_head = idx; + } + + fn pop_small(&mut self) -> u32 { + let popped = self.smalls_head; + self.smalls_head = self.aliases[popped as usize]; + popped + } + + fn pop_big(&mut self) -> u32 { + let popped = self.bigs_head; + self.bigs_head = self.aliases[popped as usize]; + popped + } + + fn smalls_is_empty(&self) -> bool { + self.smalls_head == ::core::u32::MAX + } + + fn bigs_is_empty(&self) -> bool { + self.bigs_head == ::core::u32::MAX + } + + fn set_alias(&mut self, idx: u32, alias: u32) { + self.aliases[idx as usize] = alias; + } + } + + let mut aliases = Aliases::new(n); + + // Split indices into those with small weights and those with big weights. + for (index, &odds) in no_alias_odds.iter().enumerate() { + if odds < weight_sum { + aliases.push_small(index as u32); + } else { + aliases.push_big(index as u32); + } + } + + // Build the alias map by finding an alias with big weight for each index with + // small weight. + while !aliases.smalls_is_empty() && !aliases.bigs_is_empty() { + let s = aliases.pop_small(); + let b = aliases.pop_big(); + + aliases.set_alias(s, b); + no_alias_odds[b as usize] = no_alias_odds[b as usize] + - weight_sum + + no_alias_odds[s as usize]; + + if no_alias_odds[b as usize] < weight_sum { + aliases.push_small(b); + } else { + aliases.push_big(b); + } + } + + // The remaining indices should have no alias odds of about 100%. This is due to + // numeric accuracy. Otherwise they would be exactly 100%. + while !aliases.smalls_is_empty() { + no_alias_odds[aliases.pop_small() as usize] = weight_sum; + } + while !aliases.bigs_is_empty() { + no_alias_odds[aliases.pop_big() as usize] = weight_sum; + } + + // Prepare distributions for sampling. Creating them beforehand improves + // sampling performance. + let uniform_index = Uniform::new(0, n); + let uniform_within_weight_sum = Uniform::new(W::ZERO, weight_sum); + + Ok(Self { + aliases: aliases.aliases, + no_alias_odds, + uniform_index, + uniform_within_weight_sum, + }) + } +} + +impl<W: Weight> Distribution<usize> for WeightedIndex<W> { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> usize { + let candidate = rng.sample(self.uniform_index); + if rng.sample(&self.uniform_within_weight_sum) < self.no_alias_odds[candidate as usize] { + candidate as usize + } else { + self.aliases[candidate as usize] as usize + } + } +} + +impl<W: Weight> fmt::Debug for WeightedIndex<W> +where + W: fmt::Debug, + Uniform<W>: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("WeightedIndex") + .field("aliases", &self.aliases) + .field("no_alias_odds", &self.no_alias_odds) + .field("uniform_index", &self.uniform_index) + .field("uniform_within_weight_sum", &self.uniform_within_weight_sum) + .finish() + } +} + +impl<W: Weight> Clone for WeightedIndex<W> +where + Uniform<W>: Clone, +{ + fn clone(&self) -> Self { + Self { + aliases: self.aliases.clone(), + no_alias_odds: self.no_alias_odds.clone(), + uniform_index: self.uniform_index.clone(), + uniform_within_weight_sum: self.uniform_within_weight_sum.clone(), + } + } +} + +/// Trait that must be implemented for weights, that are used with +/// [`WeightedIndex`]. Currently no guarantees on the correctness of +/// [`WeightedIndex`] are given for custom implementations of this trait. +pub trait Weight: + Sized + + Copy + + SampleUniform + + PartialOrd + + Add<Output = Self> + + AddAssign + + Sub<Output = Self> + + SubAssign + + Mul<Output = Self> + + MulAssign + + Div<Output = Self> + + DivAssign + + Sum +{ + /// Maximum number representable by `Self`. + const MAX: Self; + + /// Element of `Self` equivalent to 0. + const ZERO: Self; + + /// Produce an instance of `Self` from a `u32` value, or return `None` if + /// out of range. Loss of precision (where `Self` is a floating point type) + /// is acceptable. + fn try_from_u32_lossy(n: u32) -> Option<Self>; + + /// Sums all values in slice `values`. + fn sum(values: &[Self]) -> Self { + values.iter().map(|x| *x).sum() + } +} + +macro_rules! impl_weight_for_float { + ($T: ident) => { + impl Weight for $T { + const MAX: Self = ::core::$T::MAX; + const ZERO: Self = 0.0; + + fn try_from_u32_lossy(n: u32) -> Option<Self> { + Some(n as $T) + } + + fn sum(values: &[Self]) -> Self { + pairwise_sum(values) + } + } + }; +} + +/// In comparison to naive accumulation, the pairwise sum algorithm reduces +/// rounding errors when there are many floating point values. +fn pairwise_sum<T: Weight>(values: &[T]) -> T { + if values.len() <= 32 { + values.iter().map(|x| *x).sum() + } else { + let mid = values.len() / 2; + let (a, b) = values.split_at(mid); + pairwise_sum(a) + pairwise_sum(b) + } +} + +macro_rules! impl_weight_for_int { + ($T: ident) => { + impl Weight for $T { + const MAX: Self = ::core::$T::MAX; + const ZERO: Self = 0; + + fn try_from_u32_lossy(n: u32) -> Option<Self> { + let n_converted = n as Self; + if n_converted >= Self::ZERO && n_converted as u32 == n { + Some(n_converted) + } else { + None + } + } + } + }; +} + +impl_weight_for_float!(f64); +impl_weight_for_float!(f32); +impl_weight_for_int!(usize); +#[cfg(not(target_os = "emscripten"))] +impl_weight_for_int!(u128); +impl_weight_for_int!(u64); +impl_weight_for_int!(u32); +impl_weight_for_int!(u16); +impl_weight_for_int!(u8); +impl_weight_for_int!(isize); +#[cfg(not(target_os = "emscripten"))] +impl_weight_for_int!(i128); +impl_weight_for_int!(i64); +impl_weight_for_int!(i32); +impl_weight_for_int!(i16); +impl_weight_for_int!(i8); + +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[cfg(not(miri))] // Miri is too slow + fn test_weighted_index_f32() { + test_weighted_index(f32::into); + + // Floating point special cases + assert_eq!( + WeightedIndex::new(vec![::core::f32::INFINITY]).unwrap_err(), + WeightedError::InvalidWeight + ); + assert_eq!( + WeightedIndex::new(vec![-0_f32]).unwrap_err(), + WeightedError::AllWeightsZero + ); + assert_eq!( + WeightedIndex::new(vec![-1_f32]).unwrap_err(), + WeightedError::InvalidWeight + ); + assert_eq!( + WeightedIndex::new(vec![-::core::f32::INFINITY]).unwrap_err(), + WeightedError::InvalidWeight + ); + assert_eq!( + WeightedIndex::new(vec![::core::f32::NAN]).unwrap_err(), + WeightedError::InvalidWeight + ); + } + + #[cfg(not(target_os = "emscripten"))] + #[test] + #[cfg(not(miri))] // Miri is too slow + fn test_weighted_index_u128() { + test_weighted_index(|x: u128| x as f64); + } + + #[cfg(all(rustc_1_26, not(target_os = "emscripten")))] + #[test] + #[cfg(not(miri))] // Miri is too slow + fn test_weighted_index_i128() { + test_weighted_index(|x: i128| x as f64); + + // Signed integer special cases + assert_eq!( + WeightedIndex::new(vec![-1_i128]).unwrap_err(), + WeightedError::InvalidWeight + ); + assert_eq!( + WeightedIndex::new(vec![::core::i128::MIN]).unwrap_err(), + WeightedError::InvalidWeight + ); + } + + #[test] + #[cfg(not(miri))] // Miri is too slow + fn test_weighted_index_u8() { + test_weighted_index(u8::into); + } + + #[test] + #[cfg(not(miri))] // Miri is too slow + fn test_weighted_index_i8() { + test_weighted_index(i8::into); + + // Signed integer special cases + assert_eq!( + WeightedIndex::new(vec![-1_i8]).unwrap_err(), + WeightedError::InvalidWeight + ); + assert_eq!( + WeightedIndex::new(vec![::core::i8::MIN]).unwrap_err(), + WeightedError::InvalidWeight + ); + } + + fn test_weighted_index<W: Weight, F: Fn(W) -> f64>(w_to_f64: F) + where + WeightedIndex<W>: fmt::Debug, + { + const NUM_WEIGHTS: u32 = 10; + const ZERO_WEIGHT_INDEX: u32 = 3; + const NUM_SAMPLES: u32 = 15000; + let mut rng = crate::test::rng(0x9c9fa0b0580a7031); + + let weights = { + let mut weights = Vec::with_capacity(NUM_WEIGHTS as usize); + let random_weight_distribution = crate::distributions::Uniform::new_inclusive( + W::ZERO, + W::MAX / W::try_from_u32_lossy(NUM_WEIGHTS).unwrap(), + ); + for _ in 0..NUM_WEIGHTS { + weights.push(rng.sample(&random_weight_distribution)); + } + weights[ZERO_WEIGHT_INDEX as usize] = W::ZERO; + weights + }; + let weight_sum = weights.iter().map(|w| *w).sum::<W>(); + let expected_counts = weights + .iter() + .map(|&w| w_to_f64(w) / w_to_f64(weight_sum) * NUM_SAMPLES as f64) + .collect::<Vec<f64>>(); + let weight_distribution = WeightedIndex::new(weights).unwrap(); + + let mut counts = vec![0; NUM_WEIGHTS as usize]; + for _ in 0..NUM_SAMPLES { + counts[rng.sample(&weight_distribution)] += 1; + } + + assert_eq!(counts[ZERO_WEIGHT_INDEX as usize], 0); + for (count, expected_count) in counts.into_iter().zip(expected_counts) { + let difference = (count as f64 - expected_count).abs(); + let max_allowed_difference = NUM_SAMPLES as f64 / NUM_WEIGHTS as f64 * 0.1; + assert!(difference <= max_allowed_difference); + } + + assert_eq!( + WeightedIndex::<W>::new(vec![]).unwrap_err(), + WeightedError::NoItem + ); + assert_eq!( + WeightedIndex::new(vec![W::ZERO]).unwrap_err(), + WeightedError::AllWeightsZero + ); + assert_eq!( + WeightedIndex::new(vec![W::MAX, W::MAX]).unwrap_err(), + WeightedError::InvalidWeight + ); + } +} diff --git a/rand/src/distributions/weighted.rs b/rand/src/distributions/weighted/mod.rs index 01c8fe6..2711637 100644 --- a/rand/src/distributions/weighted.rs +++ b/rand/src/distributions/weighted/mod.rs @@ -6,14 +6,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use Rng; -use distributions::Distribution; -use distributions::uniform::{UniformSampler, SampleUniform, SampleBorrow}; -use ::core::cmp::PartialOrd; +//! Weighted index sampling +//! +//! This module provides two implementations for sampling indices: +//! +//! * [`WeightedIndex`] allows `O(log N)` sampling +//! * [`alias_method::WeightedIndex`] allows `O(1)` sampling, but with +//! much greater set-up cost +//! +//! [`alias_method::WeightedIndex`]: alias_method/struct.WeightedIndex.html + +pub mod alias_method; + +use crate::Rng; +use crate::distributions::Distribution; +use crate::distributions::uniform::{UniformSampler, SampleUniform, SampleBorrow}; +use core::cmp::PartialOrd; use core::fmt; // Note that this whole module is only imported if feature="alloc" is enabled. -#[cfg(not(feature="std"))] use alloc::vec::Vec; +#[cfg(not(feature="std"))] use crate::alloc::vec::Vec; /// A distribution using weighted sampling to pick a discretely selected /// item. @@ -40,9 +52,9 @@ use core::fmt; /// `N` is the number of weights. /// /// Sampling from `WeightedIndex` will result in a single call to -/// [`Uniform<X>::sample`], which typically will request a single value from -/// the underlying [`RngCore`], though the exact number depends on the -/// implementaiton of [`Uniform<X>::sample`]. +/// `Uniform<X>::sample` (method of the [`Distribution`] trait), which typically +/// will request a single value from the underlying [`RngCore`], though the +/// exact number depends on the implementaiton of `Uniform<X>::sample`. /// /// # Example /// @@ -67,12 +79,12 @@ use core::fmt; /// } /// ``` /// -/// [`Uniform<X>`]: struct.Uniform.html -/// [`Uniform<X>::sample`]: struct.Uniform.html#method.sample -/// [`RngCore`]: ../trait.RngCore.html +/// [`Uniform<X>`]: crate::distributions::uniform::Uniform +/// [`RngCore`]: crate::RngCore #[derive(Debug, Clone)] pub struct WeightedIndex<X: SampleUniform + PartialOrd> { cumulative_weights: Vec<X>, + total_weight: X, weight_distribution: X::Sampler, } @@ -84,8 +96,7 @@ impl<X: SampleUniform + PartialOrd> WeightedIndex<X> { /// Returns an error if the iterator is empty, if any weight is `< 0`, or /// if its total value is 0. /// - /// [`Distribution`]: trait.Distribution.html - /// [`Uniform<X>`]: struct.Uniform.html + /// [`Uniform<X>`]: crate::distributions::uniform::Uniform pub fn new<I>(weights: I) -> Result<WeightedIndex<X>, WeightedError> where I: IntoIterator, I::Item: SampleBorrow<X>, @@ -100,13 +111,13 @@ impl<X: SampleUniform + PartialOrd> WeightedIndex<X> { let zero = <X as Default>::default(); if total_weight < zero { - return Err(WeightedError::NegativeWeight); + return Err(WeightedError::InvalidWeight); } let mut weights = Vec::<X>::with_capacity(iter.size_hint().0); for w in iter { if *w.borrow() < zero { - return Err(WeightedError::NegativeWeight); + return Err(WeightedError::InvalidWeight); } weights.push(total_weight.clone()); total_weight += w.borrow(); @@ -115,9 +126,98 @@ impl<X: SampleUniform + PartialOrd> WeightedIndex<X> { if total_weight == zero { return Err(WeightedError::AllWeightsZero); } - let distr = X::Sampler::new(zero, total_weight); + let distr = X::Sampler::new(zero, total_weight.clone()); - Ok(WeightedIndex { cumulative_weights: weights, weight_distribution: distr }) + Ok(WeightedIndex { cumulative_weights: weights, total_weight, weight_distribution: distr }) + } + + /// Update a subset of weights, without changing the number of weights. + /// + /// `new_weights` must be sorted by the index. + /// + /// Using this method instead of `new` might be more efficient if only a small number of + /// weights is modified. No allocations are performed, unless the weight type `X` uses + /// allocation internally. + /// + /// In case of error, `self` is not modified. + pub fn update_weights(&mut self, new_weights: &[(usize, &X)]) -> Result<(), WeightedError> + where X: for<'a> ::core::ops::AddAssign<&'a X> + + for<'a> ::core::ops::SubAssign<&'a X> + + Clone + + Default { + if new_weights.is_empty() { + return Ok(()); + } + + let zero = <X as Default>::default(); + + let mut total_weight = self.total_weight.clone(); + + // Check for errors first, so we don't modify `self` in case something + // goes wrong. + let mut prev_i = None; + for &(i, w) in new_weights { + if let Some(old_i) = prev_i { + if old_i >= i { + return Err(WeightedError::InvalidWeight); + } + } + if *w < zero { + return Err(WeightedError::InvalidWeight); + } + if i >= self.cumulative_weights.len() + 1 { + return Err(WeightedError::TooMany); + } + + let mut old_w = if i < self.cumulative_weights.len() { + self.cumulative_weights[i].clone() + } else { + self.total_weight.clone() + }; + if i > 0 { + old_w -= &self.cumulative_weights[i - 1]; + } + + total_weight -= &old_w; + total_weight += w; + prev_i = Some(i); + } + if total_weight == zero { + return Err(WeightedError::AllWeightsZero); + } + + // Update the weights. Because we checked all the preconditions in the + // previous loop, this should never panic. + let mut iter = new_weights.iter(); + + let mut prev_weight = zero.clone(); + let mut next_new_weight = iter.next(); + let &(first_new_index, _) = next_new_weight.unwrap(); + let mut cumulative_weight = if first_new_index > 0 { + self.cumulative_weights[first_new_index - 1].clone() + } else { + zero.clone() + }; + for i in first_new_index..self.cumulative_weights.len() { + match next_new_weight { + Some(&(j, w)) if i == j => { + cumulative_weight += w; + next_new_weight = iter.next(); + }, + _ => { + let mut tmp = self.cumulative_weights[i].clone(); + tmp -= &prev_weight; // We know this is positive. + cumulative_weight += &tmp; + } + } + prev_weight = cumulative_weight.clone(); + core::mem::swap(&mut prev_weight, &mut self.cumulative_weights[i]); + } + + self.total_weight = total_weight; + self.weight_distribution = X::Sampler::new(zero, self.total_weight.clone()); + + Ok(()) } } @@ -137,8 +237,9 @@ mod test { use super::*; #[test] + #[cfg(not(miri))] // Miri is too slow fn test_weightedindex() { - let mut r = ::test::rng(700); + let mut r = crate::test::rng(700); const N_REPS: u32 = 5000; let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]; let total_weight = weights.iter().sum::<u32>() as f32; @@ -186,31 +287,61 @@ mod test { assert_eq!(WeightedIndex::new(&[10][0..0]).unwrap_err(), WeightedError::NoItem); assert_eq!(WeightedIndex::new(&[0]).unwrap_err(), WeightedError::AllWeightsZero); - assert_eq!(WeightedIndex::new(&[10, 20, -1, 30]).unwrap_err(), WeightedError::NegativeWeight); - assert_eq!(WeightedIndex::new(&[-10, 20, 1, 30]).unwrap_err(), WeightedError::NegativeWeight); - assert_eq!(WeightedIndex::new(&[-10]).unwrap_err(), WeightedError::NegativeWeight); + assert_eq!(WeightedIndex::new(&[10, 20, -1, 30]).unwrap_err(), WeightedError::InvalidWeight); + assert_eq!(WeightedIndex::new(&[-10, 20, 1, 30]).unwrap_err(), WeightedError::InvalidWeight); + assert_eq!(WeightedIndex::new(&[-10]).unwrap_err(), WeightedError::InvalidWeight); + } + + #[test] + fn test_update_weights() { + let data = [ + (&[10u32, 2, 3, 4][..], + &[(1, &100), (2, &4)][..], // positive change + &[10, 100, 4, 4][..]), + (&[1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7][..], + &[(2, &1), (5, &1), (13, &100)][..], // negative change and last element + &[1u32, 2, 1, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 100][..]), + ]; + + for (weights, update, expected_weights) in data.into_iter() { + let total_weight = weights.iter().sum::<u32>(); + let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); + assert_eq!(distr.total_weight, total_weight); + + distr.update_weights(update).unwrap(); + let expected_total_weight = expected_weights.iter().sum::<u32>(); + let expected_distr = WeightedIndex::new(expected_weights.to_vec()).unwrap(); + assert_eq!(distr.total_weight, expected_total_weight); + assert_eq!(distr.total_weight, expected_distr.total_weight); + assert_eq!(distr.cumulative_weights, expected_distr.cumulative_weights); + } } } /// Error type returned from `WeightedIndex::new`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WeightedError { - /// The provided iterator contained no items. + /// The provided weight collection contains no items. NoItem, - /// A weight lower than zero was used. - NegativeWeight, + /// A weight is either less than zero, greater than the supported maximum or + /// otherwise invalid. + InvalidWeight, - /// All items in the provided iterator had a weight of zero. + /// All items in the provided weight collection are zero. AllWeightsZero, + + /// Too many weights are provided (length greater than `u32::MAX`) + TooMany, } impl WeightedError { fn msg(&self) -> &str { match *self { - WeightedError::NoItem => "No items found", - WeightedError::NegativeWeight => "Item has negative weight", - WeightedError::AllWeightsZero => "All items had weight zero", + WeightedError::NoItem => "No weights provided.", + WeightedError::InvalidWeight => "A weight is invalid.", + WeightedError::AllWeightsZero => "All weights are zero.", + WeightedError::TooMany => "Too many weights (hit u32::MAX)", } } } @@ -220,7 +351,7 @@ impl ::std::error::Error for WeightedError { fn description(&self) -> &str { self.msg() } - fn cause(&self) -> Option<&::std::error::Error> { + fn cause(&self) -> Option<&dyn (::std::error::Error)> { None } } diff --git a/rand/src/lib.rs b/rand/src/lib.rs index ca231b5..b4167c3 100644 --- a/rand/src/lib.rs +++ b/rand/src/lib.rs @@ -17,7 +17,7 @@ //! To get you started quickly, the easiest and highest-level way to get //! a random value is to use [`random()`]; alternatively you can use //! [`thread_rng()`]. The [`Rng`] trait provides a useful API on all RNGs, while -//! the [`distributions` module] and [`seq` module] provide further +//! the [`distributions`] and [`seq`] modules provide further //! functionality on top of RNGs. //! //! ``` @@ -39,12 +39,6 @@ //! //! For the user guide and futher documentation, please read //! [The Rust Rand Book](https://rust-random.github.io/book). -//! -//! [`distributions` module]: distributions/index.html -//! [`random()`]: fn.random.html -//! [`Rng`]: trait.Rng.html -//! [`seq` module]: seq/index.html -//! [`thread_rng()`]: fn.thread_rng.html #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", @@ -56,112 +50,64 @@ #![doc(test(attr(allow(unused_variables), deny(warnings))))] #![cfg_attr(not(feature="std"), no_std)] -#![cfg_attr(all(feature="alloc", not(feature="std")), feature(alloc))] #![cfg_attr(all(feature="simd_support", feature="nightly"), feature(stdsimd))] -#[cfg(feature = "std")] extern crate core; -#[cfg(all(feature = "alloc", not(feature="std")))] #[macro_use] extern crate alloc; - -#[cfg(feature="simd_support")] extern crate packed_simd; +#![allow(clippy::excessive_precision, clippy::unreadable_literal, clippy::float_cmp)] -#[cfg(feature = "rand_os")] -extern crate rand_os; +#[cfg(all(feature="alloc", not(feature="std")))] +extern crate alloc; -extern crate rand_core; -extern crate rand_isaac; // only for deprecations -extern crate rand_chacha; // only for deprecations -extern crate rand_hc; -extern crate rand_pcg; -extern crate rand_xorshift; +#[cfg(feature = "getrandom")] +use getrandom_package as getrandom; -#[cfg(feature = "log")] #[macro_use] extern crate log; #[allow(unused)] -#[cfg(not(feature = "log"))] macro_rules! trace { ($($x:tt)*) => () } +macro_rules! trace { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::trace!($($x)*) + } +) } #[allow(unused)] -#[cfg(not(feature = "log"))] macro_rules! debug { ($($x:tt)*) => () } +macro_rules! debug { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::debug!($($x)*) + } +) } #[allow(unused)] -#[cfg(not(feature = "log"))] macro_rules! info { ($($x:tt)*) => () } +macro_rules! info { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::info!($($x)*) + } +) } #[allow(unused)] -#[cfg(not(feature = "log"))] macro_rules! warn { ($($x:tt)*) => () } +macro_rules! warn { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::warn!($($x)*) + } +) } #[allow(unused)] -#[cfg(not(feature = "log"))] macro_rules! error { ($($x:tt)*) => () } - +macro_rules! error { ($($x:tt)*) => ( + #[cfg(feature = "log")] { + log::error!($($x)*) + } +) } // Re-exports from rand_core -pub use rand_core::{RngCore, CryptoRng, SeedableRng}; -pub use rand_core::{ErrorKind, Error}; +pub use rand_core::{RngCore, CryptoRng, SeedableRng, Error}; // Public exports -#[cfg(feature="std")] pub use rngs::thread::thread_rng; +#[cfg(feature="std")] pub use crate::rngs::thread::thread_rng; // Public modules pub mod distributions; pub mod prelude; -#[deprecated(since="0.6.0")] -pub mod prng; pub mod rngs; pub mod seq; -//////////////////////////////////////////////////////////////////////////////// -// Compatibility re-exports. Documentation is hidden; will be removed eventually. - -#[doc(hidden)] mod deprecated; - -#[allow(deprecated)] -#[doc(hidden)] pub use deprecated::ReseedingRng; - -#[allow(deprecated)] -#[cfg(feature="std")] #[doc(hidden)] pub use deprecated::EntropyRng; - -#[allow(deprecated)] -#[cfg(feature="rand_os")] -#[doc(hidden)] -pub use deprecated::OsRng; - -#[allow(deprecated)] -#[doc(hidden)] pub use deprecated::{ChaChaRng, IsaacRng, Isaac64Rng, XorShiftRng}; -#[allow(deprecated)] -#[doc(hidden)] pub use deprecated::StdRng; - - -#[allow(deprecated)] -#[doc(hidden)] -pub mod jitter { - pub use deprecated::JitterRng; - pub use rngs::TimerError; -} -#[allow(deprecated)] -#[cfg(feature="rand_os")] -#[doc(hidden)] -pub mod os { - pub use deprecated::OsRng; -} -#[allow(deprecated)] -#[doc(hidden)] -pub mod chacha { - pub use deprecated::ChaChaRng; -} -#[allow(deprecated)] -#[doc(hidden)] -pub mod isaac { - pub use deprecated::{IsaacRng, Isaac64Rng}; -} -#[allow(deprecated)] -#[cfg(feature="std")] -#[doc(hidden)] -pub mod read { - pub use deprecated::ReadRng; -} - -#[allow(deprecated)] -#[cfg(feature="std")] #[doc(hidden)] pub use deprecated::ThreadRng; - -//////////////////////////////////////////////////////////////////////////////// - use core::{mem, slice}; -use distributions::{Distribution, Standard}; -use distributions::uniform::{SampleUniform, UniformSampler, SampleBorrow}; +use core::num::Wrapping; +use crate::distributions::{Distribution, Standard}; +use crate::distributions::uniform::{SampleUniform, UniformSampler, SampleBorrow}; /// An automatically-implemented extension trait on [`RngCore`] providing high-level /// generic methods for sampling values and other convenience methods. @@ -200,13 +146,9 @@ use distributions::uniform::{SampleUniform, UniformSampler, SampleBorrow}; /// /// # let v = foo(&mut thread_rng()); /// ``` -/// -/// [`RngCore`]: trait.RngCore.html pub trait Rng: RngCore { /// Return a random value supporting the [`Standard`] distribution. /// - /// [`Standard`]: distributions/struct.Standard.html - /// /// # Example /// /// ``` @@ -217,8 +159,31 @@ pub trait Rng: RngCore { /// println!("{}", x); /// println!("{:?}", rng.gen::<(f64, bool)>()); /// ``` + /// + /// # Arrays and tuples + /// + /// The `rng.gen()` method is able to generate arrays (up to 32 elements) + /// and tuples (up to 12 elements), so long as all element types can be + /// generated. + /// + /// For arrays of integers, especially for those with small element types + /// (< 64 bit), it will likely be faster to instead use [`Rng::fill`]. + /// + /// ``` + /// use rand::{thread_rng, Rng}; + /// + /// let mut rng = thread_rng(); + /// let tuple: (u8, i32, char) = rng.gen(); // arbitrary tuple support + /// + /// let arr1: [f32; 32] = rng.gen(); // array construction + /// let mut arr2 = [0u8; 128]; + /// rng.fill(&mut arr2); // array fill + /// ``` + /// + /// [`Standard`]: distributions::Standard #[inline] - fn gen<T>(&mut self) -> T where Standard: Distribution<T> { + fn gen<T>(&mut self) -> T + where Standard: Distribution<T> { Standard.sample(self) } @@ -245,10 +210,12 @@ pub trait Rng: RngCore { /// println!("{}", m); /// ``` /// - /// [`Uniform`]: distributions/uniform/struct.Uniform.html + /// [`Uniform`]: distributions::uniform::Uniform fn gen_range<T: SampleUniform, B1, B2>(&mut self, low: B1, high: B2) -> T - where B1: SampleBorrow<T> + Sized, - B2: SampleBorrow<T> + Sized { + where + B1: SampleBorrow<T> + Sized, + B2: SampleBorrow<T> + Sized, + { T::Sampler::sample_single(low, high, self) } @@ -272,34 +239,39 @@ pub trait Rng: RngCore { /// Create an iterator that generates values using the given distribution. /// + /// Note that this function takes its arguments by value. This works since + /// `(&mut R): Rng where R: Rng` and + /// `(&D): Distribution where D: Distribution`, + /// however borrowing is not automatic hence `rng.sample_iter(...)` may + /// need to be replaced with `(&mut rng).sample_iter(...)`. + /// /// # Example /// /// ``` /// use rand::{thread_rng, Rng}; /// use rand::distributions::{Alphanumeric, Uniform, Standard}; /// - /// let mut rng = thread_rng(); + /// let rng = thread_rng(); /// /// // Vec of 16 x f32: - /// let v: Vec<f32> = thread_rng().sample_iter(&Standard).take(16).collect(); + /// let v: Vec<f32> = rng.sample_iter(Standard).take(16).collect(); /// /// // String: - /// let s: String = rng.sample_iter(&Alphanumeric).take(7).collect(); + /// let s: String = rng.sample_iter(Alphanumeric).take(7).collect(); /// /// // Combined values - /// println!("{:?}", thread_rng().sample_iter(&Standard).take(5) + /// println!("{:?}", rng.sample_iter(Standard).take(5) /// .collect::<Vec<(f64, bool)>>()); /// /// // Dice-rolling: /// let die_range = Uniform::new_inclusive(1, 6); - /// let mut roll_die = rng.sample_iter(&die_range); + /// let mut roll_die = rng.sample_iter(die_range); /// while roll_die.next().unwrap() != 6 { /// println!("Not a 6; rolling again!"); /// } /// ``` - fn sample_iter<'a, T, D: Distribution<T>>(&'a mut self, distr: &'a D) - -> distributions::DistIter<'a, D, Self, T> where Self: Sized - { + fn sample_iter<T, D>(self, distr: D) -> distributions::DistIter<D, Self, T> + where D: Distribution<T>, Self: Sized { distr.sample_iter(self) } @@ -323,9 +295,8 @@ pub trait Rng: RngCore { /// thread_rng().fill(&mut arr[..]); /// ``` /// - /// [`fill_bytes`]: trait.RngCore.html#method.fill_bytes - /// [`try_fill`]: trait.Rng.html#method.try_fill - /// [`AsByteSliceMut`]: trait.AsByteSliceMut.html + /// [`fill_bytes`]: RngCore::fill_bytes + /// [`try_fill`]: Rng::try_fill fn fill<T: AsByteSliceMut + ?Sized>(&mut self, dest: &mut T) { self.fill_bytes(dest.as_byte_slice_mut()); dest.to_le(); @@ -338,10 +309,8 @@ pub trait Rng: RngCore { /// On big-endian platforms this performs byte-swapping to ensure /// portability of results from reproducible generators. /// - /// This uses [`try_fill_bytes`] internally and forwards all RNG errors. In - /// some cases errors may be resolvable; see [`ErrorKind`] and - /// documentation for the RNG in use. If you do not plan to handle these - /// errors you may prefer to use [`fill`]. + /// This is identical to [`fill`] except that it uses [`try_fill_bytes`] + /// internally and forwards RNG errors. /// /// # Example /// @@ -358,10 +327,8 @@ pub trait Rng: RngCore { /// # try_inner().unwrap() /// ``` /// - /// [`ErrorKind`]: enum.ErrorKind.html - /// [`try_fill_bytes`]: trait.RngCore.html#method.try_fill_bytes - /// [`fill`]: trait.Rng.html#method.fill - /// [`AsByteSliceMut`]: trait.AsByteSliceMut.html + /// [`try_fill_bytes`]: RngCore::try_fill_bytes + /// [`fill`]: Rng::fill fn try_fill<T: AsByteSliceMut + ?Sized>(&mut self, dest: &mut T) -> Result<(), Error> { self.try_fill_bytes(dest.as_byte_slice_mut())?; dest.to_le(); @@ -386,10 +353,10 @@ pub trait Rng: RngCore { /// /// If `p < 0` or `p > 1`. /// - /// [`Bernoulli`]: distributions/bernoulli/struct.Bernoulli.html + /// [`Bernoulli`]: distributions::bernoulli::Bernoulli #[inline] fn gen_bool(&mut self, p: f64) -> bool { - let d = distributions::Bernoulli::new(p); + let d = distributions::Bernoulli::new(p).unwrap(); self.sample(d) } @@ -415,55 +382,19 @@ pub trait Rng: RngCore { /// println!("{}", rng.gen_ratio(2, 3)); /// ``` /// - /// [`Bernoulli`]: distributions/bernoulli/struct.Bernoulli.html + /// [`Bernoulli`]: distributions::bernoulli::Bernoulli #[inline] fn gen_ratio(&mut self, numerator: u32, denominator: u32) -> bool { - let d = distributions::Bernoulli::from_ratio(numerator, denominator); + let d = distributions::Bernoulli::from_ratio(numerator, denominator).unwrap(); self.sample(d) } - - /// Return a random element from `values`. - /// - /// Deprecated: use [`SliceRandom::choose`] instead. - /// - /// [`SliceRandom::choose`]: seq/trait.SliceRandom.html#method.choose - #[deprecated(since="0.6.0", note="use SliceRandom::choose instead")] - fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> { - use seq::SliceRandom; - values.choose(self) - } - - /// Return a mutable pointer to a random element from `values`. - /// - /// Deprecated: use [`SliceRandom::choose_mut`] instead. - /// - /// [`SliceRandom::choose_mut`]: seq/trait.SliceRandom.html#method.choose_mut - #[deprecated(since="0.6.0", note="use SliceRandom::choose_mut instead")] - fn choose_mut<'a, T>(&mut self, values: &'a mut [T]) -> Option<&'a mut T> { - use seq::SliceRandom; - values.choose_mut(self) - } - - /// Shuffle a mutable slice in place. - /// - /// Deprecated: use [`SliceRandom::shuffle`] instead. - /// - /// [`SliceRandom::shuffle`]: seq/trait.SliceRandom.html#method.shuffle - #[deprecated(since="0.6.0", note="use SliceRandom::shuffle instead")] - fn shuffle<T>(&mut self, values: &mut [T]) { - use seq::SliceRandom; - values.shuffle(self) - } } impl<R: RngCore + ?Sized> Rng for R {} /// Trait for casting types to byte slices /// -/// This is used by the [`fill`] and [`try_fill`] methods. -/// -/// [`fill`]: trait.Rng.html#method.fill -/// [`try_fill`]: trait.Rng.html#method.try_fill +/// This is used by the [`Rng::fill`] and [`Rng::try_fill`] methods. pub trait AsByteSliceMut { /// Return a mutable reference to self as a byte slice fn as_byte_slice_mut(&mut self) -> &mut [u8]; @@ -481,6 +412,7 @@ impl AsByteSliceMut for [u8] { } macro_rules! impl_as_byte_slice { + () => {}; ($t:ty) => { impl AsByteSliceMut for [$t] { fn as_byte_slice_mut(&mut self) -> &mut [u8] { @@ -491,8 +423,7 @@ macro_rules! impl_as_byte_slice { } } else { unsafe { - slice::from_raw_parts_mut(&mut self[0] - as *mut $t + slice::from_raw_parts_mut(self.as_mut_ptr() as *mut u8, self.len() * mem::size_of::<$t>() ) @@ -506,26 +437,47 @@ macro_rules! impl_as_byte_slice { } } } + + impl AsByteSliceMut for [Wrapping<$t>] { + fn as_byte_slice_mut(&mut self) -> &mut [u8] { + if self.len() == 0 { + unsafe { + // must not use null pointer + slice::from_raw_parts_mut(0x1 as *mut u8, 0) + } + } else { + unsafe { + slice::from_raw_parts_mut(self.as_mut_ptr() + as *mut u8, + self.len() * mem::size_of::<$t>() + ) + } + } + } + + fn to_le(&mut self) { + for x in self { + *x = Wrapping(x.0.to_le()); + } + } + } + }; + ($t:ty, $($tt:ty,)*) => { + impl_as_byte_slice!($t); + // TODO: this could replace above impl once Rust #32463 is fixed + // impl_as_byte_slice!(Wrapping<$t>); + impl_as_byte_slice!($($tt,)*); } } -impl_as_byte_slice!(u16); -impl_as_byte_slice!(u32); -impl_as_byte_slice!(u64); -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] impl_as_byte_slice!(u128); -impl_as_byte_slice!(usize); -impl_as_byte_slice!(i8); -impl_as_byte_slice!(i16); -impl_as_byte_slice!(i32); -impl_as_byte_slice!(i64); -#[cfg(all(rustc_1_26, not(target_os = "emscripten")))] impl_as_byte_slice!(i128); -impl_as_byte_slice!(isize); +impl_as_byte_slice!(u16, u32, u64, usize,); +#[cfg(not(target_os = "emscripten"))] impl_as_byte_slice!(u128); +impl_as_byte_slice!(i8, i16, i32, i64, isize,); +#[cfg(not(target_os = "emscripten"))] impl_as_byte_slice!(i128); macro_rules! impl_as_byte_slice_arrays { ($n:expr,) => {}; - ($n:expr, $N:ident, $($NN:ident,)*) => { - impl_as_byte_slice_arrays!($n - 1, $($NN,)*); - + ($n:expr, $N:ident) => { impl<T> AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut { fn as_byte_slice_mut(&mut self) -> &mut [u8] { self[..].as_byte_slice_mut() @@ -536,96 +488,19 @@ macro_rules! impl_as_byte_slice_arrays { } } }; + ($n:expr, $N:ident, $($NN:ident,)*) => { + impl_as_byte_slice_arrays!($n, $N); + impl_as_byte_slice_arrays!($n - 1, $($NN,)*); + }; (!div $n:expr,) => {}; (!div $n:expr, $N:ident, $($NN:ident,)*) => { + impl_as_byte_slice_arrays!($n, $N); impl_as_byte_slice_arrays!(!div $n / 2, $($NN,)*); - - impl<T> AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut { - fn as_byte_slice_mut(&mut self) -> &mut [u8] { - self[..].as_byte_slice_mut() - } - - fn to_le(&mut self) { - self[..].to_le() - } - } }; } impl_as_byte_slice_arrays!(32, N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,); impl_as_byte_slice_arrays!(!div 4096, N,N,N,N,N,N,N,); - -/// A convenience extension to [`SeedableRng`] allowing construction from fresh -/// entropy. This trait is automatically implemented for any PRNG implementing -/// [`SeedableRng`] and is not intended to be implemented by users. -/// -/// This is equivalent to using `SeedableRng::from_rng(EntropyRng::new())` then -/// unwrapping the result. -/// -/// Since this is convenient and secure, it is the recommended way to create -/// PRNGs, though two alternatives may be considered: -/// -/// * Deterministic creation using [`SeedableRng::from_seed`] with a fixed seed -/// * Seeding from `thread_rng`: `SeedableRng::from_rng(thread_rng())?`; -/// this will usually be faster and should also be secure, but requires -/// trusting one extra component. -/// -/// ## Example -/// -/// ``` -/// use rand::{Rng, FromEntropy}; -/// use rand::rngs::StdRng; -/// -/// let mut rng = StdRng::from_entropy(); -/// println!("Random die roll: {}", rng.gen_range(1, 7)); -/// ``` -/// -/// [`EntropyRng`]: rngs/struct.EntropyRng.html -/// [`SeedableRng`]: trait.SeedableRng.html -/// [`SeedableRng::from_seed`]: trait.SeedableRng.html#tymethod.from_seed -#[cfg(feature="std")] -pub trait FromEntropy: SeedableRng { - /// Creates a new instance, automatically seeded with fresh entropy. - /// - /// Normally this will use `OsRng`, but if that fails `JitterRng` will be - /// used instead. Both should be suitable for cryptography. It is possible - /// that both entropy sources will fail though unlikely; failures would - /// almost certainly be platform limitations or build issues, i.e. most - /// applications targetting PC/mobile platforms should not need to worry - /// about this failing. - /// - /// # Panics - /// - /// If all entropy sources fail this will panic. If you need to handle - /// errors, use the following code, equivalent aside from error handling: - /// - /// ``` - /// # use rand::Error; - /// use rand::prelude::*; - /// use rand::rngs::EntropyRng; - /// - /// # fn try_inner() -> Result<(), Error> { - /// // This uses StdRng, but is valid for any R: SeedableRng - /// let mut rng = StdRng::from_rng(EntropyRng::new())?; - /// - /// println!("random number: {}", rng.gen_range(1, 10)); - /// # Ok(()) - /// # } - /// - /// # try_inner().unwrap() - /// ``` - fn from_entropy() -> Self; -} - -#[cfg(feature="std")] -impl<R: SeedableRng> FromEntropy for R { - fn from_entropy() -> R { - R::from_rng(rngs::EntropyRng::new()).unwrap_or_else(|err| - panic!("FromEntropy::from_entropy() failed: {}", err)) - } -} - - /// Generates a random value using the thread-local random number generator. /// /// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for @@ -667,40 +542,26 @@ impl<R: SeedableRng> FromEntropy for R { /// } /// ``` /// -/// [`thread_rng`]: fn.thread_rng.html -/// [`Standard`]: distributions/struct.Standard.html +/// [`Standard`]: distributions::Standard #[cfg(feature="std")] #[inline] -pub fn random<T>() -> T where Standard: Distribution<T> { +pub fn random<T>() -> T +where Standard: Distribution<T> { thread_rng().gen() } #[cfg(test)] mod test { - use rngs::mock::StepRng; - use rngs::StdRng; + use crate::rngs::mock::StepRng; use super::*; #[cfg(all(not(feature="std"), feature="alloc"))] use alloc::boxed::Box; - pub struct TestRng<R> { inner: R } - - impl<R: RngCore> RngCore for TestRng<R> { - fn next_u32(&mut self) -> u32 { - self.inner.next_u32() - } - fn next_u64(&mut self) -> u64 { - self.inner.next_u64() - } - fn fill_bytes(&mut self, dest: &mut [u8]) { - self.inner.fill_bytes(dest) - } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.inner.try_fill_bytes(dest) - } - } - - pub fn rng(seed: u64) -> TestRng<StdRng> { - TestRng { inner: StdRng::seed_from_u64(seed) } + /// Construct a deterministic RNG with the given seed + pub fn rng(seed: u64) -> impl RngCore { + // For tests, we want a statistically good, fast, reproducible RNG. + // PCG32 will do fine, and will be easy to embed if we ever need to. + const INC: u64 = 11634580027462260723; + rand_pcg::Pcg32::new(seed, INC) } #[test] @@ -740,6 +601,12 @@ mod test { rng.fill(&mut array[..]); assert_eq!(array, [x as u32, (x >> 32) as u32]); assert_eq!(rng.next_u32(), x as u32); + + // Check equivalence using wrapped arrays + let mut warray = [Wrapping(0u32); 2]; + rng.fill(&mut warray[..]); + assert_eq!(array[0], warray[0].0); + assert_eq!(array[1], warray[1].0); } #[test] @@ -796,9 +663,9 @@ mod test { #[test] fn test_rng_trait_object() { - use distributions::{Distribution, Standard}; + use crate::distributions::{Distribution, Standard}; let mut rng = rng(109); - let mut r = &mut rng as &mut RngCore; + let mut r = &mut rng as &mut dyn RngCore; r.next_u32(); r.gen::<i32>(); assert_eq!(r.gen_range(0, 1), 0); @@ -808,9 +675,9 @@ mod test { #[test] #[cfg(feature="alloc")] fn test_rng_boxed_trait() { - use distributions::{Distribution, Standard}; + use crate::distributions::{Distribution, Standard}; let rng = rng(110); - let mut r = Box::new(rng) as Box<RngCore>; + let mut r = Box::new(rng) as Box<dyn RngCore>; r.next_u32(); r.gen::<i32>(); assert_eq!(r.gen_range(0, 1), 0); @@ -833,6 +700,7 @@ mod test { } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_gen_ratio_average() { const NUM: u32 = 3; const DENOM: u32 = 10; diff --git a/rand/src/prelude.rs b/rand/src/prelude.rs index 5d8a0e9..3c386e8 100644 --- a/rand/src/prelude.rs +++ b/rand/src/prelude.rs @@ -14,14 +14,15 @@ //! //! ``` //! use rand::prelude::*; -//! # let _ = StdRng::from_entropy(); -//! # let mut r = SmallRng::from_rng(thread_rng()).unwrap(); +//! # let mut r = StdRng::from_rng(thread_rng()).unwrap(); //! # let _: f32 = r.gen(); //! ``` -#[doc(no_inline)] pub use distributions::Distribution; -#[doc(no_inline)] pub use rngs::{SmallRng, StdRng}; -#[doc(no_inline)] #[cfg(feature="std")] pub use rngs::ThreadRng; -#[doc(no_inline)] pub use {Rng, RngCore, CryptoRng, SeedableRng}; -#[doc(no_inline)] #[cfg(feature="std")] pub use {FromEntropy, random, thread_rng}; -#[doc(no_inline)] pub use seq::{SliceRandom, IteratorRandom}; +#[doc(no_inline)] pub use crate::distributions::Distribution; +#[doc(no_inline)] pub use crate::rngs::StdRng; +#[cfg(feature="small_rng")] +#[doc(no_inline)] pub use crate::rngs::SmallRng; +#[doc(no_inline)] #[cfg(feature="std")] pub use crate::rngs::ThreadRng; +#[doc(no_inline)] pub use crate::{Rng, RngCore, CryptoRng, SeedableRng}; +#[doc(no_inline)] #[cfg(feature="std")] pub use crate::{random, thread_rng}; +#[doc(no_inline)] pub use crate::seq::{SliceRandom, IteratorRandom}; diff --git a/rand/src/prng/mod.rs b/rand/src/prng/mod.rs deleted file mode 100644 index 3c0d27b..0000000 --- a/rand/src/prng/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Pseudo-random number generators. -//! -//! This module is deprecated: -//! -//! - documentation has moved to -//! [The Book](https://rust-random.github.io/book/guide-rngs.html), -//! - PRNGs have moved to other `rand_*` crates. - -// Deprecations (to be removed in 0.7) -#[doc(hidden)] #[allow(deprecated)] -pub use deprecated::XorShiftRng; -#[doc(hidden)] pub mod isaac { - // Note: we miss `IsaacCore` here but probably unimportant. - #[allow(deprecated)] pub use deprecated::IsaacRng; -} -#[doc(hidden)] pub mod isaac64 { - #[allow(deprecated)] pub use deprecated::Isaac64Rng; -} -#[doc(hidden)] #[allow(deprecated)] pub use deprecated::{IsaacRng, Isaac64Rng}; -#[doc(hidden)] pub mod chacha { - // Note: we miss `ChaChaCore` here but probably unimportant. - #[allow(deprecated)] pub use deprecated::ChaChaRng; -} -#[doc(hidden)] #[allow(deprecated)] pub use deprecated::ChaChaRng; -#[doc(hidden)] pub mod hc128 { - // Note: we miss `Hc128Core` here but probably unimportant. - #[allow(deprecated)] pub use deprecated::Hc128Rng; -} -#[doc(hidden)] #[allow(deprecated)] pub use deprecated::Hc128Rng; diff --git a/rand/src/rngs/adapter/mod.rs b/rand/src/rngs/adapter/mod.rs index 60b832e..659ff26 100644 --- a/rand/src/rngs/adapter/mod.rs +++ b/rand/src/rngs/adapter/mod.rs @@ -8,8 +8,8 @@ //! Wrappers / adapters forming RNGs -#[cfg(feature="std")] #[doc(hidden)] pub mod read; +#[cfg(feature="std")] mod read; mod reseeding; -#[cfg(feature="std")] pub use self::read::ReadRng; +#[cfg(feature="std")] pub use self::read::{ReadRng, ReadError}; pub use self::reseeding::ReseedingRng; diff --git a/rand/src/rngs/adapter/read.rs b/rand/src/rngs/adapter/read.rs index 30b6de6..901462e 100644 --- a/rand/src/rngs/adapter/read.rs +++ b/rand/src/rngs/adapter/read.rs @@ -10,12 +10,13 @@ //! A wrapper around any Read to treat it as an RNG. use std::io::Read; +use std::fmt; -use rand_core::{RngCore, Error, ErrorKind, impls}; +use rand_core::{RngCore, Error, impls}; /// An RNG that reads random bytes straight from any type supporting -/// `std::io::Read`, for example files. +/// [`std::io::Read`], for example files. /// /// This will work best with an infinite reader, but that is not required. /// @@ -24,10 +25,10 @@ use rand_core::{RngCore, Error, ErrorKind, impls}; /// /// # Panics /// -/// `ReadRng` uses `std::io::read_exact`, which retries on interrupts. All other -/// errors from the underlying reader, including when it does not have enough -/// data, will only be reported through [`try_fill_bytes`]. The other -/// [`RngCore`] methods will panic in case of an error. +/// `ReadRng` uses [`std::io::Read::read_exact`], which retries on interrupts. +/// All other errors from the underlying reader, including when it does not +/// have enough data, will only be reported through [`try_fill_bytes`]. +/// The other [`RngCore`] methods will panic in case of an error. /// /// # Example /// @@ -40,9 +41,8 @@ use rand_core::{RngCore, Error, ErrorKind, impls}; /// println!("{:x}", rng.gen::<u32>()); /// ``` /// -/// [`OsRng`]: ../struct.OsRng.html -/// [`RngCore`]: ../../trait.RngCore.html -/// [`try_fill_bytes`]: ../../trait.RngCore.html#method.tymethod.try_fill_bytes +/// [`OsRng`]: crate::rngs::OsRng +/// [`try_fill_bytes`]: RngCore::try_fill_bytes #[derive(Debug)] pub struct ReadRng<R> { reader: R @@ -72,24 +72,33 @@ impl<R: Read> RngCore for ReadRng<R> { } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - if dest.len() == 0 { return Ok(()); } + if dest.is_empty() { return Ok(()); } // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. - self.reader.read_exact(dest).map_err(|err| { - match err.kind() { - ::std::io::ErrorKind::UnexpectedEof => Error::with_cause( - ErrorKind::Unavailable, - "not enough bytes available, reached end of source", err), - _ => Error::with_cause(ErrorKind::Unavailable, - "error reading from Read source", err) - } - }) + self.reader.read_exact(dest).map_err(|e| Error::new(ReadError(e))) } } +/// `ReadRng` error type +#[derive(Debug)] +pub struct ReadError(std::io::Error); + +impl fmt::Display for ReadError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ReadError: {}", self.0) + } +} + +impl std::error::Error for ReadError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.0) + } +} + + #[cfg(test)] mod test { use super::ReadRng; - use {RngCore, ErrorKind}; + use crate::RngCore; #[test] fn test_reader_rng_u64() { @@ -132,6 +141,8 @@ mod test { let mut rng = ReadRng::new(&v[..]); - assert!(rng.try_fill_bytes(&mut w).err().unwrap().kind == ErrorKind::Unavailable); + let result = rng.try_fill_bytes(&mut w); + assert!(result.is_err()); + println!("Error: {}", result.unwrap_err()); } } diff --git a/rand/src/rngs/adapter/reseeding.rs b/rand/src/rngs/adapter/reseeding.rs index 016afab..ec88efe 100644 --- a/rand/src/rngs/adapter/reseeding.rs +++ b/rand/src/rngs/adapter/reseeding.rs @@ -12,7 +12,7 @@ use core::mem::size_of; -use rand_core::{RngCore, CryptoRng, SeedableRng, Error, ErrorKind}; +use rand_core::{RngCore, CryptoRng, SeedableRng, Error}; use rand_core::block::{BlockRngCore, BlockRng}; /// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the @@ -24,7 +24,7 @@ use rand_core::block::{BlockRngCore, BlockRng}; /// - After `clone()`, the clone will be reseeded on first use. /// - After a process is forked, the RNG in the child process is reseeded within /// the next few generated values, depending on the block size of the -/// underlying PRNG. For [`ChaChaCore`] and [`Hc128Core`] this is a maximum of +/// underlying PRNG. For ChaCha and Hc128 this is a maximum of /// 15 `u32` values before reseeding. /// - After the PRNG has generated a configurable number of random bytes. /// @@ -57,33 +57,24 @@ use rand_core::block::{BlockRngCore, BlockRng}; /// # Example /// /// ``` -/// # extern crate rand; -/// # extern crate rand_chacha; -/// # fn main() { /// use rand::prelude::*; -/// use rand_chacha::ChaChaCore; // Internal part of ChaChaRng that +/// use rand_chacha::ChaCha20Core; // Internal part of ChaChaRng that /// // implements BlockRngCore /// use rand::rngs::OsRng; /// use rand::rngs::adapter::ReseedingRng; /// -/// let prng = ChaChaCore::from_entropy(); -// FIXME: it is better to use EntropyRng as reseeder, but that doesn't implement -// clone yet. -/// let reseeder = OsRng::new().unwrap(); -/// let mut reseeding_rng = ReseedingRng::new(prng, 0, reseeder); +/// let prng = ChaCha20Core::from_entropy(); +/// let mut reseeding_rng = ReseedingRng::new(prng, 0, OsRng); /// /// println!("{}", reseeding_rng.gen::<u64>()); /// /// let mut cloned_rng = reseeding_rng.clone(); /// assert!(reseeding_rng.gen::<u64>() != cloned_rng.gen::<u64>()); -/// # } /// ``` /// -/// [`ChaChaCore`]: ../../../rand_chacha/struct.ChaChaCore.html -/// [`Hc128Core`]: ../../../rand_hc/struct.Hc128Core.html -/// [`BlockRngCore`]: ../../../rand_core/block/trait.BlockRngCore.html -/// [`ReseedingRng::new`]: struct.ReseedingRng.html#method.new -/// [`reseed()`]: struct.ReseedingRng.html#method.reseed +/// [`BlockRngCore`]: rand_core::block::BlockRngCore +/// [`ReseedingRng::new`]: ReseedingRng::new +/// [`reseed()`]: ReseedingRng::reseed #[derive(Debug)] pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>) where R: BlockRngCore + SeedableRng, @@ -234,6 +225,7 @@ where R: BlockRngCore + SeedableRng, results: &mut <Self as BlockRngCore>::Results, global_fork_counter: usize) { + #![allow(clippy::if_same_then_else)] // false positive if self.is_forked(global_fork_counter) { info!("Fork detected, reseeding RNG"); } else { @@ -243,21 +235,13 @@ where R: BlockRngCore + SeedableRng, let num_bytes = results.as_ref().len() * size_of::<<R as BlockRngCore>::Item>(); - let threshold = if let Err(e) = self.reseed() { - let delay = match e.kind { - ErrorKind::Transient => num_bytes as i64, - kind @ _ if kind.should_retry() => self.threshold >> 8, - _ => self.threshold, - }; - warn!("Reseeding RNG delayed reseeding by {} bytes due to \ - error from source: {}", delay, e); - delay - } else { - self.fork_counter = global_fork_counter; - self.threshold - }; + if let Err(e) = self.reseed() { + warn!("Reseeding RNG failed: {}", e); + let _ = e; + } + self.fork_counter = global_fork_counter; - self.bytes_until_reseed = threshold - num_bytes as i64; + self.bytes_until_reseed = self.threshold - num_bytes as i64; self.inner.generate(results); } } @@ -282,12 +266,11 @@ where R: BlockRngCore + SeedableRng + CryptoRng, Rsdr: RngCore + CryptoRng {} -#[cfg(all(feature="std", unix, not(target_os="emscripten")))] +#[cfg(all(unix, not(target_os="emscripten")))] mod fork { - extern crate libc; - - use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; - use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT}; + use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering}; + #[allow(deprecated)] // Required for compatibility with Rust < 1.24. + use core::sync::atomic::{ATOMIC_USIZE_INIT, ATOMIC_BOOL_INIT}; // Fork protection // @@ -301,12 +284,14 @@ mod fork { // don't update `fork_counter`, so a reseed is attempted as soon as // possible. + #[allow(deprecated)] static RESEEDING_RNG_FORK_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; pub fn get_fork_counter() -> usize { RESEEDING_RNG_FORK_COUNTER.load(Ordering::Relaxed) } + #[allow(deprecated)] static FORK_HANDLER_REGISTERED: AtomicBool = ATOMIC_BOOL_INIT; extern fn fork_handler() { @@ -316,14 +301,14 @@ mod fork { } pub fn register_fork_handler() { - if FORK_HANDLER_REGISTERED.load(Ordering::Relaxed) == false { + if !FORK_HANDLER_REGISTERED.load(Ordering::Relaxed) { unsafe { libc::pthread_atfork(None, None, Some(fork_handler)) }; FORK_HANDLER_REGISTERED.store(true, Ordering::Relaxed); } } } -#[cfg(not(all(feature="std", unix, not(target_os="emscripten"))))] +#[cfg(not(all(unix, not(target_os="emscripten"))))] mod fork { pub fn get_fork_counter() -> usize { 0 } pub fn register_fork_handler() {} @@ -332,25 +317,27 @@ mod fork { #[cfg(test)] mod test { - use {Rng, SeedableRng}; - use rand_chacha::ChaChaCore; - use rngs::mock::StepRng; + use crate::{Rng, SeedableRng}; + use crate::rngs::std::Core; + use crate::rngs::mock::StepRng; use super::ReseedingRng; #[test] fn test_reseeding() { let mut zero = StepRng::new(0, 0); - let rng = ChaChaCore::from_rng(&mut zero).unwrap(); - let mut reseeding = ReseedingRng::new(rng, 32*4, zero); - - // Currently we only support for arrays up to length 32. - // TODO: cannot generate seq via Rng::gen because it uses different alg - let mut buf = [0u32; 32]; // Needs to be a multiple of the RNGs result - // size to test exactly. - reseeding.fill(&mut buf); + let rng = Core::from_rng(&mut zero).unwrap(); + let thresh = 1; // reseed every time the buffer is exhausted + let mut reseeding = ReseedingRng::new(rng, thresh, zero); + + // RNG buffer size is [u32; 64] + // Debug is only implemented up to length 32 so use two arrays + let mut buf = ([0u32; 32], [0u32; 32]); + reseeding.fill(&mut buf.0); + reseeding.fill(&mut buf.1); let seq = buf; for _ in 0..10 { - reseeding.fill(&mut buf); + reseeding.fill(&mut buf.0); + reseeding.fill(&mut buf.1); assert_eq!(buf, seq); } } @@ -358,7 +345,7 @@ mod test { #[test] fn test_clone_reseeding() { let mut zero = StepRng::new(0, 0); - let rng = ChaChaCore::from_rng(&mut zero).unwrap(); + let rng = Core::from_rng(&mut zero).unwrap(); let mut rng1 = ReseedingRng::new(rng, 32*4, zero); let first: u32 = rng1.gen(); diff --git a/rand/src/rngs/entropy.rs b/rand/src/rngs/entropy.rs index 372b4d7..1ed59ab 100644 --- a/rand/src/rngs/entropy.rs +++ b/rand/src/rngs/entropy.rs @@ -8,52 +8,21 @@ //! Entropy generator, or wrapper around external generators -use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls}; -#[allow(unused)] -use rngs; +#![allow(deprecated)] // whole module is deprecated + +use rand_core::{RngCore, CryptoRng, Error}; +use crate::rngs::OsRng; /// An interface returning random data from external source(s), provided /// specifically for securely seeding algorithmic generators (PRNGs). /// -/// Where possible, `EntropyRng` retrieves random data from the operating -/// system's interface for random numbers ([`OsRng`]); if that fails it will -/// fall back to the [`JitterRng`] entropy collector. In the latter case it will -/// still try to use [`OsRng`] on the next usage. -/// -/// If no secure source of entropy is available `EntropyRng` will panic on use; -/// i.e. it should never output predictable data. -/// -/// This is either a little slow ([`OsRng`] requires a system call) or extremely -/// slow ([`JitterRng`] must use significant CPU time to generate sufficient -/// jitter); for better performance it is common to seed a local PRNG from -/// external entropy then primarily use the local PRNG ([`thread_rng`] is -/// provided as a convenient, local, automatically-seeded CSPRNG). -/// -/// # Panics -/// -/// On most systems, like Windows, Linux, macOS and *BSD on common hardware, it -/// is highly unlikely for both [`OsRng`] and [`JitterRng`] to fail. But on -/// combinations like webassembly without Emscripten or stdweb both sources are -/// unavailable. If both sources fail, only [`try_fill_bytes`] is able to -/// report the error, and only the one from `OsRng`. The other [`RngCore`] -/// methods will panic in case of an error. -/// -/// [`OsRng`]: struct.OsRng.html -/// [`JitterRng`]: jitter/struct.JitterRng.html -/// [`thread_rng`]: ../fn.thread_rng.html -/// [`RngCore`]: ../trait.RngCore.html -/// [`try_fill_bytes`]: ../trait.RngCore.html#method.tymethod.try_fill_bytes +/// This is deprecated. It is suggested you use [`rngs::OsRng`] instead. +/// +/// [`rngs::OsRng`]: crate::rngs::OsRng #[derive(Debug)] +#[deprecated(since="0.7.0", note="use rngs::OsRng instead")] pub struct EntropyRng { - source: Source, -} - -#[derive(Debug)] -enum Source { - Os(Os), - Custom(Custom), - Jitter(Jitter), - None, + source: OsRng, } impl EntropyRng { @@ -63,7 +32,7 @@ impl EntropyRng { /// those are done on first use. This is done to make `new` infallible, /// and `try_fill_bytes` the only place to report errors. pub fn new() -> Self { - EntropyRng { source: Source::None } + EntropyRng { source: OsRng } } } @@ -75,167 +44,25 @@ impl Default for EntropyRng { impl RngCore for EntropyRng { fn next_u32(&mut self) -> u32 { - impls::next_u32_via_fill(self) + self.source.next_u32() } fn next_u64(&mut self) -> u64 { - impls::next_u64_via_fill(self) + self.source.next_u64() } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.try_fill_bytes(dest).unwrap_or_else(|err| - panic!("all entropy sources failed; first error: {}", err)) + self.source.fill_bytes(dest) } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - let mut reported_error = None; - - if let Source::Os(ref mut os_rng) = self.source { - match os_rng.fill(dest) { - Ok(()) => return Ok(()), - Err(err) => { - warn!("EntropyRng: OsRng failed \ - [trying other entropy sources]: {}", err); - reported_error = Some(err); - }, - } - } else if Os::is_supported() { - match Os::new_and_fill(dest) { - Ok(os_rng) => { - debug!("EntropyRng: using OsRng"); - self.source = Source::Os(os_rng); - return Ok(()); - }, - Err(err) => { reported_error = reported_error.or(Some(err)) }, - } - } - - if let Source::Custom(ref mut rng) = self.source { - match rng.fill(dest) { - Ok(()) => return Ok(()), - Err(err) => { - warn!("EntropyRng: custom entropy source failed \ - [trying other entropy sources]: {}", err); - reported_error = Some(err); - }, - } - } else if Custom::is_supported() { - match Custom::new_and_fill(dest) { - Ok(custom) => { - debug!("EntropyRng: using custom entropy source"); - self.source = Source::Custom(custom); - return Ok(()); - }, - Err(err) => { reported_error = reported_error.or(Some(err)) }, - } - } - - if let Source::Jitter(ref mut jitter_rng) = self.source { - match jitter_rng.fill(dest) { - Ok(()) => return Ok(()), - Err(err) => { - warn!("EntropyRng: JitterRng failed: {}", err); - reported_error = Some(err); - }, - } - } else if Jitter::is_supported() { - match Jitter::new_and_fill(dest) { - Ok(jitter_rng) => { - debug!("EntropyRng: using JitterRng"); - self.source = Source::Jitter(jitter_rng); - return Ok(()); - }, - Err(err) => { reported_error = reported_error.or(Some(err)) }, - } - } - - if let Some(err) = reported_error { - Err(Error::with_cause(ErrorKind::Unavailable, - "All entropy sources failed", - err)) - } else { - Err(Error::new(ErrorKind::Unavailable, - "No entropy sources available")) - } + self.source.try_fill_bytes(dest) } } impl CryptoRng for EntropyRng {} - -trait EntropySource { - fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> - where Self: Sized; - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; - - fn is_supported() -> bool { true } -} - -#[allow(unused)] -#[derive(Clone, Debug)] -struct NoSource; - -#[allow(unused)] -impl EntropySource for NoSource { - fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> { - Err(Error::new(ErrorKind::Unavailable, "Source not supported")) - } - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - unreachable!() - } - - fn is_supported() -> bool { false } -} - - -#[cfg(feature="rand_os")] -#[derive(Clone, Debug)] -pub struct Os(rngs::OsRng); - -#[cfg(feature="rand_os")] -impl EntropySource for Os { - fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> { - let mut rng = rngs::OsRng::new()?; - rng.try_fill_bytes(dest)?; - Ok(Os(rng)) - } - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(not(feature="std"))] -type Os = NoSource; - - -type Custom = NoSource; - - -#[cfg(not(target_arch = "wasm32"))] -#[derive(Clone, Debug)] -pub struct Jitter(rngs::JitterRng); - -#[cfg(not(target_arch = "wasm32"))] -impl EntropySource for Jitter { - fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> { - let mut rng = rngs::JitterRng::new()?; - rng.try_fill_bytes(dest)?; - Ok(Jitter(rng)) - } - - fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(dest) - } -} - -#[cfg(target_arch = "wasm32")] -type Jitter = NoSource; - - #[cfg(test)] mod test { use super::*; diff --git a/rand/src/rngs/mock.rs b/rand/src/rngs/mock.rs index 3c9a994..b4081da 100644 --- a/rand/src/rngs/mock.rs +++ b/rand/src/rngs/mock.rs @@ -39,21 +39,26 @@ impl StepRng { } impl RngCore for StepRng { + #[inline] fn next_u32(&mut self) -> u32 { self.next_u64() as u32 } + #[inline] fn next_u64(&mut self) -> u64 { let result = self.v; self.v = self.v.wrapping_add(self.a); result } + #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { impls::fill_bytes_via_next(self, dest); } + #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + self.fill_bytes(dest); + Ok(()) } } diff --git a/rand/src/rngs/mod.rs b/rand/src/rngs/mod.rs index 847fc94..abf3243 100644 --- a/rand/src/rngs/mod.rs +++ b/rand/src/rngs/mod.rs @@ -6,177 +6,114 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Random number generators and adapters for common usage: -//! -//! - [`ThreadRng`], a fast, secure, auto-seeded thread-local generator -//! - [`StdRng`] and [`SmallRng`], algorithms to cover typical usage -//! - [`EntropyRng`], [`OsRng`] and [`JitterRng`] as entropy sources -//! - [`mock::StepRng`] as a simple counter for tests -//! - [`adapter::ReadRng`] to read from a file/stream -//! - [`adapter::ReseedingRng`] to reseed a PRNG on clone / process fork etc. -//! -//! # Background — Random number generators (RNGs) -//! -//! Computers are inherently deterministic, so to get *random* numbers one -//! either has to use a hardware generator or collect bits of *entropy* from -//! various sources (e.g. event timestamps, or jitter). This is a relatively -//! slow and complicated operation. -//! -//! Generally the operating system will collect some entropy, remove bias, and -//! use that to seed its own PRNG; [`OsRng`] provides an interface to this. -//! [`JitterRng`] is an entropy collector included with Rand that measures -//! jitter in the CPU execution time, and jitter in memory access time. -//! [`EntropyRng`] is a wrapper that uses the best entropy source that is -//! available. -//! -//! ## Pseudo-random number generators -//! -//! What is commonly used instead of "true" random number renerators, are -//! *pseudo-random number generators* (PRNGs), deterministic algorithms that -//! produce an infinite stream of pseudo-random numbers from a small random -//! seed. PRNGs are faster, and have better provable properties. The numbers -//! produced can be statistically of very high quality and can be impossible to -//! predict. (They can also have obvious correlations and be trivial to predict; -//! quality varies.) -//! -//! There are two different types of PRNGs: those developed for simulations -//! and statistics, and those developed for use in cryptography; the latter are -//! called Cryptographically Secure PRNGs (CSPRNG or CPRNG). Both types can -//! have good statistical quality but the latter also have to be impossible to -//! predict, even after seeing many previous output values. Rand provides a good -//! default algorithm from each class: -//! -//! - [`SmallRng`] is a PRNG chosen for low memory usage, high performance and -//! good statistical quality. -//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security -//! (based on reviews, maturity and usage). The current algorithm is HC-128, -//! which is one of the recommendations by ECRYPT's eSTREAM project. -//! -//! The above PRNGs do not cover all use-cases; more algorithms can be found in -//! the [`prng` module], as well as in several other crates. For example, you -//! may wish a CSPRNG with significantly lower memory usage than [`StdRng`] -//! while being less concerned about performance, in which case [`ChaChaRng`] -//! is a good choice. -//! -//! One complexity is that the internal state of a PRNG must change with every -//! generated number. For APIs this generally means a mutable reference to the -//! state of the PRNG has to be passed around. -//! -//! A solution is [`ThreadRng`]. This is a thread-local implementation of -//! [`StdRng`] with automatic seeding on first use. It is the best choice if you -//! "just" want a convenient, secure, fast random number source. Use via the -//! [`thread_rng`] function, which gets a reference to the current thread's -//! local instance. -//! -//! ## Seeding -//! -//! As mentioned above, PRNGs require a random seed in order to produce random -//! output. This is especially important for CSPRNGs, which are still -//! deterministic algorithms, thus can only be secure if their seed value is -//! also secure. To seed a PRNG, use one of: -//! -//! - [`FromEntropy::from_entropy`]; this is the most convenient way to seed -//! with fresh, secure random data. -//! - [`SeedableRng::from_rng`]; this allows seeding from another PRNG or -//! from an entropy source such as [`EntropyRng`]. -//! - [`SeedableRng::from_seed`]; this is mostly useful if you wish to be able -//! to reproduce the output sequence by using a fixed seed. (Don't use -//! [`StdRng`] or [`SmallRng`] in this case since different algorithms may be -//! used by future versions of Rand; use an algorithm from the -//! [`prng` module].) -//! -//! ## Conclusion -//! -//! - [`thread_rng`] is what you often want to use. -//! - If you want more control, flexibility, or better performance, use -//! [`StdRng`], [`SmallRng`] or an algorithm from the [`prng` module]. -//! - Use [`FromEntropy::from_entropy`] to seed new PRNGs. -//! - If you need reproducibility, use [`SeedableRng::from_seed`] combined with -//! a named PRNG. -//! -//! More information and notes on cryptographic security can be found -//! in the [`prng` module]. -//! -//! ## Examples -//! -//! Examples of seeding PRNGs: -//! -//! ``` -//! use rand::prelude::*; -//! # use rand::Error; -//! -//! // StdRng seeded securely by the OS or local entropy collector: -//! let mut rng = StdRng::from_entropy(); -//! # let v: u32 = rng.gen(); -//! -//! // SmallRng seeded from thread_rng: -//! # fn try_inner() -> Result<(), Error> { -//! let mut rng = SmallRng::from_rng(thread_rng())?; -//! # let v: u32 = rng.gen(); -//! # Ok(()) -//! # } -//! # try_inner().unwrap(); -//! -//! // SmallRng seeded by a constant, for deterministic results: -//! let seed = [1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16]; // byte array -//! let mut rng = SmallRng::from_seed(seed); -//! # let v: u32 = rng.gen(); -//! ``` -//! -//! -//! # Implementing custom RNGs -//! -//! If you want to implement custom RNG, see the [`rand_core`] crate. The RNG -//! will have to implement the [`RngCore`] trait, where the [`Rng`] trait is -//! build on top of. -//! -//! If the RNG needs seeding, also implement the [`SeedableRng`] trait. -//! -//! [`CryptoRng`] is a marker trait cryptographically secure PRNGs can -//! implement. -//! -//! -// This module: -//! [`ThreadRng`]: struct.ThreadRng.html -//! [`StdRng`]: struct.StdRng.html -//! [`SmallRng`]: struct.SmallRng.html -//! [`EntropyRng`]: struct.EntropyRng.html -//! [`OsRng`]: struct.OsRng.html -//! [`JitterRng`]: struct.JitterRng.html -// Other traits and functions: -//! [`rand_core`]: https://crates.io/crates/rand_core -//! [`prng` module]: ../prng/index.html -//! [`CryptoRng`]: ../trait.CryptoRng.html -//! [`FromEntropy`]: ../trait.FromEntropy.html -//! [`FromEntropy::from_entropy`]: ../trait.FromEntropy.html#tymethod.from_entropy -//! [`RngCore`]: ../trait.RngCore.html -//! [`Rng`]: ../trait.Rng.html -//! [`SeedableRng`]: ../trait.SeedableRng.html -//! [`SeedableRng::from_rng`]: ../trait.SeedableRng.html#tymethod.from_rng -//! [`SeedableRng::from_seed`]: ../trait.SeedableRng.html#tymethod.from_seed -//! [`thread_rng`]: ../fn.thread_rng.html -//! [`mock::StepRng`]: mock/struct.StepRng.html -//! [`adapter::ReadRng`]: adapter/struct.ReadRng.html -//! [`adapter::ReseedingRng`]: adapter/struct.ReseedingRng.html -//! [`ChaChaRng`]: ../../rand_chacha/struct.ChaChaRng.html +//! Random number generators and adapters +//! +//! ## Background: Random number generators (RNGs) +//! +//! Computers cannot produce random numbers from nowhere. We classify +//! random number generators as follows: +//! +//! - "True" random number generators (TRNGs) use hard-to-predict data sources +//! (e.g. the high-resolution parts of event timings and sensor jitter) to +//! harvest random bit-sequences, apply algorithms to remove bias and +//! estimate available entropy, then combine these bits into a byte-sequence +//! or an entropy pool. This job is usually done by the operating system or +//! a hardware generator (HRNG). +//! - "Pseudo"-random number generators (PRNGs) use algorithms to transform a +//! seed into a sequence of pseudo-random numbers. These generators can be +//! fast and produce well-distributed unpredictable random numbers (or not). +//! They are usually deterministic: given algorithm and seed, the output +//! sequence can be reproduced. They have finite period and eventually loop; +//! with many algorithms this period is fixed and can be proven sufficiently +//! long, while others are chaotic and the period depends on the seed. +//! - "Cryptographically secure" pseudo-random number generators (CSPRNGs) +//! are the sub-set of PRNGs which are secure. Security of the generator +//! relies both on hiding the internal state and using a strong algorithm. +//! +//! ## Traits and functionality +//! +//! All RNGs implement the [`RngCore`] trait, as a consequence of which the +//! [`Rng`] extension trait is automatically implemented. Secure RNGs may +//! additionally implement the [`CryptoRng`] trait. +//! +//! All PRNGs require a seed to produce their random number sequence. The +//! [`SeedableRng`] trait provides three ways of constructing PRNGs: +//! +//! - `from_seed` accepts a type specific to the PRNG +//! - `from_rng` allows a PRNG to be seeded from any other RNG +//! - `seed_from_u64` allows any PRNG to be seeded from a `u64` insecurely +//! - `from_entropy` securely seeds a PRNG from fresh entropy +//! +//! Use the [`rand_core`] crate when implementing your own RNGs. +//! +//! ## Our generators +//! +//! This crate provides several random number generators: +//! +//! - [`OsRng`] is an interface to the operating system's random number +//! source. Typically the operating system uses a CSPRNG with entropy +//! provided by a TRNG and some type of on-going re-seeding. +//! - [`ThreadRng`], provided by the [`thread_rng`] function, is a handle to a +//! thread-local CSPRNG with periodic seeding from [`OsRng`]. Because this +//! is local, it is typically much faster than [`OsRng`]. It should be +//! secure, though the paranoid may prefer [`OsRng`]. +//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security +//! (based on reviews, maturity and usage). The current algorithm is ChaCha20, +//! which is well established and rigorously analysed. +//! [`StdRng`] provides the algorithm used by [`ThreadRng`] but without +//! periodic reseeding. +//! - [`SmallRng`] is an **insecure** PRNG designed to be fast, simple, require +//! little memory, and have good output quality. +//! +//! The algorithms selected for [`StdRng`] and [`SmallRng`] may change in any +//! release and may be platform-dependent, therefore they should be considered +//! **not reproducible**. +//! +//! ## Additional generators +//! +//! **TRNGs**: The [`rdrand`] crate provides an interface to the RDRAND and +//! RDSEED instructions available in modern Intel and AMD CPUs. +//! The [`rand_jitter`] crate provides a user-space implementation of +//! entropy harvesting from CPU timer jitter, but is very slow and has +//! [security issues](https://github.com/rust-random/rand/issues/699). +//! +//! **PRNGs**: Several companion crates are available, providing individual or +//! families of PRNG algorithms. These provide the implementations behind +//! [`StdRng`] and [`SmallRng`] but can also be used directly, indeed *should* +//! be used directly when **reproducibility** matters. +//! Some suggestions are: [`rand_chacha`], [`rand_pcg`], [`rand_xoshiro`]. +//! A full list can be found by searching for crates with the [`rng` tag]. +//! +//! [`SmallRng`]: rngs::SmallRng +//! [`StdRng`]: rngs::StdRng +//! [`OsRng`]: rngs::OsRng +//! [`ThreadRng`]: rngs::ThreadRng +//! [`mock::StepRng`]: rngs::mock::StepRng +//! [`adapter::ReadRng`]: rngs::adapter::ReadRng +//! [`adapter::ReseedingRng`]: rngs::adapter::ReseedingRng +//! [`rdrand`]: https://crates.io/crates/rdrand +//! [`rand_jitter`]: https://crates.io/crates/rand_jitter +//! [`rand_chacha`]: https://crates.io/crates/rand_chacha +//! [`rand_pcg`]: https://crates.io/crates/rand_pcg +//! [`rand_xoshiro`]: https://crates.io/crates/rand_xoshiro +//! [`rng` tag]: https://crates.io/keywords/rng pub mod adapter; #[cfg(feature="std")] mod entropy; -mod jitter; pub mod mock; // Public so we don't export `StepRng` directly, making it a bit // more clear it is intended for testing. +#[cfg(feature="small_rng")] mod small; mod std; #[cfg(feature="std")] pub(crate) mod thread; - -pub use self::jitter::{JitterRng, TimerError}; +#[allow(deprecated)] #[cfg(feature="std")] pub use self::entropy::EntropyRng; +#[cfg(feature="small_rng")] pub use self::small::SmallRng; pub use self::std::StdRng; #[cfg(feature="std")] pub use self::thread::ThreadRng; -#[cfg(feature="rand_os")] -pub use rand_os::OsRng; +#[cfg(feature="getrandom")] pub use rand_core::OsRng; diff --git a/rand/src/rngs/small.rs b/rand/src/rngs/small.rs index b652c8c..6571363 100644 --- a/rand/src/rngs/small.rs +++ b/rand/src/rngs/small.rs @@ -8,35 +8,42 @@ //! A small fast RNG -use {RngCore, SeedableRng, Error}; +use rand_core::{RngCore, SeedableRng, Error}; -#[cfg(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64"))] -type Rng = ::rand_pcg::Pcg64Mcg; -#[cfg(not(all(all(rustc_1_26, not(target_os = "emscripten")), target_pointer_width = "64")))] -type Rng = ::rand_pcg::Pcg32; +#[cfg(all(not(target_os = "emscripten"), target_pointer_width = "64"))] +type Rng = rand_pcg::Pcg64Mcg; +#[cfg(not(all(not(target_os = "emscripten"), target_pointer_width = "64")))] +type Rng = rand_pcg::Pcg32; -/// An RNG recommended when small state, cheap initialization and good -/// performance are required. The PRNG algorithm in `SmallRng` is chosen to be -/// efficient on the current platform, **without consideration for cryptography -/// or security**. The size of its state is much smaller than for [`StdRng`]. +/// A small-state, fast non-crypto PRNG /// -/// Reproducibility of output from this generator is however not required, thus -/// future library versions may use a different internal generator with -/// different output. Further, this generator may not be portable and can -/// produce different output depending on the architecture. If you require -/// reproducible output, use a named RNG. Refer to the documentation on the -/// [`prng` module](../prng/index.html). +/// `SmallRng` may be a good choice when a PRNG with small state, cheap +/// initialization, good statistical quality and good performance are required. +/// It is **not** a good choice when security against prediction or +/// reproducibility are important. /// -/// The current algorithm is [`Pcg64Mcg`] on 64-bit platforms with Rust version -/// 1.26 and later, or [`Pcg32`] otherwise. +/// This PRNG is **feature-gated**: to use, you must enable the crate feature +/// `small_rng`. +/// +/// The algorithm is deterministic but should not be considered reproducible +/// due to dependence on platform and possible replacement in future +/// library versions. For a reproducible generator, use a named PRNG from an +/// external crate, e.g. [rand_pcg] or [rand_chacha]. +/// Refer also to [The Book](https://rust-random.github.io/book/guide-rngs.html). +/// +/// The PRNG algorithm in `SmallRng` is chosen to be +/// efficient on the current platform, without consideration for cryptography +/// or security. The size of its state is much smaller than [`StdRng`]. +/// The current algorithm is [`Pcg64Mcg`](rand_pcg::Pcg64Mcg) on 64-bit +/// platforms and [`Pcg32`](rand_pcg::Pcg32) on 32-bit platforms. Both are +/// implemented by the [rand_pcg] crate. /// /// # Examples /// -/// Initializing `SmallRng` with a random seed can be done using [`FromEntropy`]: +/// Initializing `SmallRng` with a random seed can be done using [`SeedableRng::from_entropy`]: /// /// ``` -/// # use rand::Rng; -/// use rand::FromEntropy; +/// use rand::{Rng, SeedableRng}; /// use rand::rngs::SmallRng; /// /// // Create small, cheap to initialize and fast RNG with a random seed. @@ -64,11 +71,10 @@ type Rng = ::rand_pcg::Pcg32; /// .collect(); /// ``` /// -/// [`FromEntropy`]: ../trait.FromEntropy.html -/// [`StdRng`]: struct.StdRng.html -/// [`thread_rng`]: ../fn.thread_rng.html -/// [`Pcg64Mcg`]: ../../rand_pcg/type.Pcg64Mcg.html -/// [`Pcg32`]: ../../rand_pcg/type.Pcg32.html +/// [`StdRng`]: crate::rngs::StdRng +/// [`thread_rng`]: crate::thread_rng +/// [rand_chacha]: https://crates.io/crates/rand_chacha +/// [rand_pcg]: https://crates.io/crates/rand_pcg #[derive(Clone, Debug)] pub struct SmallRng(Rng); @@ -83,10 +89,12 @@ impl RngCore for SmallRng { self.0.next_u64() } + #[inline(always)] fn fill_bytes(&mut self, dest: &mut [u8]) { self.0.fill_bytes(dest); } + #[inline(always)] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.0.try_fill_bytes(dest) } @@ -95,10 +103,12 @@ impl RngCore for SmallRng { impl SeedableRng for SmallRng { type Seed = <Rng as SeedableRng>::Seed; + #[inline(always)] fn from_seed(seed: Self::Seed) -> Self { SmallRng(Rng::from_seed(seed)) } + #[inline(always)] fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { Rng::from_rng(rng).map(SmallRng) } diff --git a/rand/src/rngs/std.rs b/rand/src/rngs/std.rs index ce1658b..22e08ae 100644 --- a/rand/src/rngs/std.rs +++ b/rand/src/rngs/std.rs @@ -8,25 +8,30 @@ //! The standard RNG -use {RngCore, CryptoRng, Error, SeedableRng}; -use rand_hc::Hc128Rng; +use crate::{RngCore, CryptoRng, Error, SeedableRng}; + +#[cfg(target_os = "emscripten")] pub(crate) use rand_hc::Hc128Core as Core; +#[cfg(not(target_os = "emscripten"))] pub(crate) use rand_chacha::ChaCha20Core as Core; +#[cfg(target_os = "emscripten")] use rand_hc::Hc128Rng as Rng; +#[cfg(not(target_os = "emscripten"))] use rand_chacha::ChaCha20Rng as Rng; /// The standard RNG. The PRNG algorithm in `StdRng` is chosen to be efficient /// on the current platform, to be statistically strong and unpredictable /// (meaning a cryptographically secure PRNG). /// -/// The current algorithm used on all platforms is [HC-128]. +/// The current algorithm used is the ChaCha block cipher with either 20 or 12 +/// rounds (see the `stdrng_*` feature flags, documented in the README). +/// This may change as new evidence of cipher security and performance +/// becomes available. /// -/// Reproducibility of output from this generator is however not required, thus -/// future library versions may use a different internal generator with -/// different output. Further, this generator may not be portable and can -/// produce different output depending on the architecture. If you require -/// reproducible output, use a named RNG, for example [`ChaChaRng`]. +/// The algorithm is deterministic but should not be considered reproducible +/// due to dependence on configuration and possible replacement in future +/// library versions. For a secure reproducible generator, we recommend use of +/// the [rand_chacha] crate directly. /// -/// [HC-128]: ../../rand_hc/struct.Hc128Rng.html -/// [`ChaChaRng`]: ../../rand_chacha/struct.ChaChaRng.html +/// [rand_chacha]: https://crates.io/crates/rand_chacha #[derive(Clone, Debug)] -pub struct StdRng(Hc128Rng); +pub struct StdRng(Rng); impl RngCore for StdRng { #[inline(always)] @@ -39,24 +44,28 @@ impl RngCore for StdRng { self.0.next_u64() } + #[inline(always)] fn fill_bytes(&mut self, dest: &mut [u8]) { self.0.fill_bytes(dest); } + #[inline(always)] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { self.0.try_fill_bytes(dest) } } impl SeedableRng for StdRng { - type Seed = <Hc128Rng as SeedableRng>::Seed; + type Seed = <Rng as SeedableRng>::Seed; + #[inline(always)] fn from_seed(seed: Self::Seed) -> Self { - StdRng(Hc128Rng::from_seed(seed)) + StdRng(Rng::from_seed(seed)) } + #[inline(always)] fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { - Hc128Rng::from_rng(rng).map(StdRng) + Rng::from_rng(rng).map(StdRng) } } @@ -65,17 +74,27 @@ impl CryptoRng for StdRng {} #[cfg(test)] mod test { - use {RngCore, SeedableRng}; - use rngs::StdRng; + use crate::{RngCore, SeedableRng}; + use crate::rngs::StdRng; #[test] fn test_stdrng_construction() { + // Test value-stability of StdRng. This is expected to break any time + // the algorithm is changed. let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; - let mut rng1 = StdRng::from_seed(seed); - assert_eq!(rng1.next_u64(), 15759097995037006553); - let mut rng2 = StdRng::from_rng(rng1).unwrap(); - assert_eq!(rng2.next_u64(), 6766915756997287454); + #[cfg(any(feature="stdrng_strong", not(feature="stdrng_fast")))] + let target = [3950704604716924505, 5573172343717151650]; + #[cfg(all(not(feature="stdrng_strong"), feature="stdrng_fast"))] + let target = [10719222850664546238, 14064965282130556830]; + + let mut rng0 = StdRng::from_seed(seed); + let x0 = rng0.next_u64(); + + let mut rng1 = StdRng::from_rng(rng0).unwrap(); + let x1 = rng1.next_u64(); + + assert_eq!([x0, x1], target); } } diff --git a/rand/src/rngs/thread.rs b/rand/src/rngs/thread.rs index 7977d85..2006f41 100644 --- a/rand/src/rngs/thread.rs +++ b/rand/src/rngs/thread.rs @@ -9,11 +9,12 @@ //! Thread-local random number generator use std::cell::UnsafeCell; +use std::ptr::NonNull; -use {RngCore, CryptoRng, SeedableRng, Error}; -use rngs::adapter::ReseedingRng; -use rngs::EntropyRng; -use rand_hc::Hc128Core; +use crate::{RngCore, CryptoRng, SeedableRng, Error}; +use crate::rngs::adapter::ReseedingRng; +use crate::rngs::OsRng; +use super::std::Core; // Rationale for using `UnsafeCell` in `ThreadRng`: // @@ -28,61 +29,43 @@ use rand_hc::Hc128Core; // completely under our control. We just have to ensure none of them use // `ThreadRng` internally, which is nonsensical anyway. We should also never run // `ThreadRng` in destructors of its implementation, which is also nonsensical. -// -// The additional `Rc` is not strictly neccesary, and could be removed. For now -// it ensures `ThreadRng` stays `!Send` and `!Sync`, and implements `Clone`. -// Number of generated bytes after which to reseed `TreadRng`. -// -// The time it takes to reseed HC-128 is roughly equivalent to generating 7 KiB. -// We pick a treshold here that is large enough to not reduce the average -// performance too much, but also small enough to not make reseeding something -// that basically never happens. -const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB +// Number of generated bytes after which to reseed `ThreadRng`. +// According to benchmarks, reseeding has a noticable impact with thresholds +// of 32 kB and less. We choose 64 kB to avoid significant overhead. +const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; /// The type returned by [`thread_rng`], essentially just a reference to the /// PRNG in thread-local memory. /// -/// `ThreadRng` uses [`ReseedingRng`] wrapping the same PRNG as [`StdRng`], -/// which is reseeded after generating 32 MiB of random data. A single instance -/// is cached per thread and the returned `ThreadRng` is a reference to this -/// instance — hence `ThreadRng` is neither `Send` nor `Sync` but is safe to use -/// within a single thread. This RNG is seeded and reseeded via [`EntropyRng`] -/// as required. +/// `ThreadRng` uses the same PRNG as [`StdRng`] for security and performance. +/// As hinted by the name, the generator is thread-local. `ThreadRng` is a +/// handle to this generator and thus supports `Copy`, but not `Send` or `Sync`. /// -/// Note that the reseeding is done as an extra precaution against entropy -/// leaks and is in theory unnecessary — to predict `ThreadRng`'s output, an -/// attacker would have to either determine most of the RNG's seed or internal -/// state, or crack the algorithm used. +/// Unlike `StdRng`, `ThreadRng` uses the [`ReseedingRng`] wrapper to reseed +/// the PRNG from fresh entropy every 64 kiB of random data. +/// [`OsRng`] is used to provide seed data. /// -/// Like [`StdRng`], `ThreadRng` is a cryptographically secure PRNG. The current -/// algorithm used is [HC-128], which is an array-based PRNG that trades memory -/// usage for better performance. This makes it similar to ISAAC, the algorithm -/// used in `ThreadRng` before rand 0.5. +/// Note that the reseeding is done as an extra precaution against side-channel +/// attacks and mis-use (e.g. if somehow weak entropy were supplied initially). +/// The PRNG algorithms used are assumed to be secure. /// -/// Cloning this handle just produces a new reference to the same thread-local -/// generator. -/// -/// [`thread_rng`]: ../fn.thread_rng.html -/// [`ReseedingRng`]: adapter/struct.ReseedingRng.html -/// [`StdRng`]: struct.StdRng.html -/// [`EntropyRng`]: struct.EntropyRng.html -/// [HC-128]: ../../rand_hc/struct.Hc128Rng.html -#[derive(Clone, Debug)] +/// [`ReseedingRng`]: crate::rngs::adapter::ReseedingRng +/// [`StdRng`]: crate::rngs::StdRng +#[derive(Copy, Clone, Debug)] pub struct ThreadRng { - // use of raw pointer implies type is neither Send nor Sync - rng: *mut ReseedingRng<Hc128Core, EntropyRng>, + // inner raw pointer implies type is neither Send nor Sync + rng: NonNull<ReseedingRng<Core, OsRng>>, } thread_local!( - static THREAD_RNG_KEY: UnsafeCell<ReseedingRng<Hc128Core, EntropyRng>> = { - let mut entropy_source = EntropyRng::new(); - let r = Hc128Core::from_rng(&mut entropy_source).unwrap_or_else(|err| + static THREAD_RNG_KEY: UnsafeCell<ReseedingRng<Core, OsRng>> = { + let r = Core::from_rng(OsRng).unwrap_or_else(|err| panic!("could not initialize thread_rng: {}", err)); let rng = ReseedingRng::new(r, THREAD_RNG_RESEED_THRESHOLD, - entropy_source); + OsRng); UnsafeCell::new(rng) } ); @@ -91,38 +74,38 @@ thread_local!( /// seeded by the system. Intended to be used in method chaining style, /// e.g. `thread_rng().gen::<i32>()`, or cached locally, e.g. /// `let mut rng = thread_rng();`. Invoked by the `Default` trait, making -/// `ThreadRng::default()` equivelent. +/// `ThreadRng::default()` equivalent. /// /// For more information see [`ThreadRng`]. -/// -/// [`ThreadRng`]: rngs/struct.ThreadRng.html pub fn thread_rng() -> ThreadRng { - ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.get()) } + let raw = THREAD_RNG_KEY.with(|t| t.get()); + let nn = NonNull::new(raw).unwrap(); + ThreadRng { rng: nn } } impl Default for ThreadRng { fn default() -> ThreadRng { - ::prelude::thread_rng() + crate::prelude::thread_rng() } } impl RngCore for ThreadRng { #[inline(always)] fn next_u32(&mut self) -> u32 { - unsafe { (*self.rng).next_u32() } + unsafe { self.rng.as_mut().next_u32() } } #[inline(always)] fn next_u64(&mut self) -> u64 { - unsafe { (*self.rng).next_u64() } + unsafe { self.rng.as_mut().next_u64() } } fn fill_bytes(&mut self, dest: &mut [u8]) { - unsafe { (*self.rng).fill_bytes(dest) } + unsafe { self.rng.as_mut().fill_bytes(dest) } } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - unsafe { (*self.rng).try_fill_bytes(dest) } + unsafe { self.rng.as_mut().try_fill_bytes(dest) } } } @@ -133,8 +116,8 @@ impl CryptoRng for ThreadRng {} mod test { #[test] fn test_thread_rng() { - use Rng; - let mut r = ::thread_rng(); + use crate::Rng; + let mut r = crate::thread_rng(); r.gen::<i32>(); assert_eq!(r.gen_range(0, 1), 0); } diff --git a/rand/src/seq/index.rs b/rand/src/seq/index.rs index 3d4df3a..22a5733 100644 --- a/rand/src/seq/index.rs +++ b/rand/src/seq/index.rs @@ -6,18 +6,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Index sampling +//! Low-level API for sampling indices #[cfg(feature="alloc")] use core::slice; #[cfg(feature="std")] use std::vec; -#[cfg(all(feature="alloc", not(feature="std")))] use alloc::vec::{self, Vec}; +#[cfg(all(feature="alloc", not(feature="std")))] use crate::alloc::vec::{self, Vec}; // BTreeMap is not as fast in tests, but better than nothing. #[cfg(feature="std")] use std::collections::{HashSet}; -#[cfg(all(feature="alloc", not(feature="std")))] use alloc::collections::BTreeSet; +#[cfg(all(feature="alloc", not(feature="std")))] use crate::alloc::collections::BTreeSet; -#[cfg(feature="alloc")] use distributions::{Distribution, Uniform}; -use Rng; +#[cfg(feature="alloc")] use crate::distributions::{Distribution, Uniform, uniform::SampleUniform}; +use crate::Rng; /// A vector of indices. /// @@ -30,25 +30,37 @@ pub enum IndexVec { impl IndexVec { /// Returns the number of indices + #[inline] pub fn len(&self) -> usize { - match self { - &IndexVec::U32(ref v) => v.len(), - &IndexVec::USize(ref v) => v.len(), + match *self { + IndexVec::U32(ref v) => v.len(), + IndexVec::USize(ref v) => v.len(), + } + } + + /// Returns `true` if the length is 0. + #[inline] + pub fn is_empty(&self) -> bool { + match *self { + IndexVec::U32(ref v) => v.is_empty(), + IndexVec::USize(ref v) => v.is_empty(), } } /// Return the value at the given `index`. /// - /// (Note: we cannot implement `std::ops::Index` because of lifetime + /// (Note: we cannot implement [`std::ops::Index`] because of lifetime /// restrictions.) + #[inline] pub fn index(&self, index: usize) -> usize { - match self { - &IndexVec::U32(ref v) => v[index] as usize, - &IndexVec::USize(ref v) => v[index], + match *self { + IndexVec::U32(ref v) => v[index] as usize, + IndexVec::USize(ref v) => v[index], } } /// Return result as a `Vec<usize>`. Conversion may or may not be trivial. + #[inline] pub fn into_vec(self) -> Vec<usize> { match self { IndexVec::U32(v) => v.into_iter().map(|i| i as usize).collect(), @@ -57,14 +69,16 @@ impl IndexVec { } /// Iterate over the indices as a sequence of `usize` values - pub fn iter<'a>(&'a self) -> IndexVecIter<'a> { - match self { - &IndexVec::U32(ref v) => IndexVecIter::U32(v.iter()), - &IndexVec::USize(ref v) => IndexVecIter::USize(v.iter()), + #[inline] + pub fn iter(&self) -> IndexVecIter<'_> { + match *self { + IndexVec::U32(ref v) => IndexVecIter::U32(v.iter()), + IndexVec::USize(ref v) => IndexVecIter::USize(v.iter()), } } /// Convert into an iterator over the indices as a sequence of `usize` values + #[inline] pub fn into_iter(self) -> IndexVecIntoIter { match self { IndexVec::U32(v) => IndexVecIntoIter::U32(v.into_iter()), @@ -88,12 +102,14 @@ impl PartialEq for IndexVec { } impl From<Vec<u32>> for IndexVec { + #[inline] fn from(v: Vec<u32>) -> Self { IndexVec::U32(v) } } impl From<Vec<usize>> for IndexVec { + #[inline] fn from(v: Vec<usize>) -> Self { IndexVec::USize(v) } @@ -108,18 +124,20 @@ pub enum IndexVecIter<'a> { impl<'a> Iterator for IndexVecIter<'a> { type Item = usize; + #[inline] fn next(&mut self) -> Option<usize> { use self::IndexVecIter::*; - match self { - &mut U32(ref mut iter) => iter.next().map(|i| *i as usize), - &mut USize(ref mut iter) => iter.next().cloned(), + match *self { + U32(ref mut iter) => iter.next().map(|i| *i as usize), + USize(ref mut iter) => iter.next().cloned(), } } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - match self { - &IndexVecIter::U32(ref v) => v.size_hint(), - &IndexVecIter::USize(ref v) => v.size_hint(), + match *self { + IndexVecIter::U32(ref v) => v.size_hint(), + IndexVecIter::USize(ref v) => v.size_hint(), } } } @@ -136,19 +154,21 @@ pub enum IndexVecIntoIter { impl Iterator for IndexVecIntoIter { type Item = usize; + #[inline] fn next(&mut self) -> Option<Self::Item> { use self::IndexVecIntoIter::*; - match self { - &mut U32(ref mut v) => v.next().map(|i| i as usize), - &mut USize(ref mut v) => v.next(), + match *self { + U32(ref mut v) => v.next().map(|i| i as usize), + USize(ref mut v) => v.next(), } } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { use self::IndexVecIntoIter::*; - match self { - &U32(ref v) => v.size_hint(), - &USize(ref v) => v.size_hint(), + match *self { + U32(ref v) => v.size_hint(), + USize(ref v) => v.size_hint(), } } } @@ -173,14 +193,13 @@ impl ExactSizeIterator for IndexVecIntoIter {} /// Note that performance is significantly better over `u32` indices than over /// `u64` indices. Because of this we hide the underlying type behind an /// abstraction, `IndexVec`. -/// +/// /// If an allocation-free `no_std` function is required, it is suggested /// to adapt the internal `sample_floyd` implementation. /// /// Panics if `amount > length`. pub fn sample<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec - where R: Rng + ?Sized, -{ +where R: Rng + ?Sized { if amount > length { panic!("`amount` of samples must be less than or equal to `length`"); } @@ -213,9 +232,7 @@ pub fn sample<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec if (length as f32) < C[j] * (amount as f32) { sample_inplace(rng, length, amount) } else { - // note: could have a specific u32 impl, but I'm lazy and - // generics don't have usable conversions - sample_rejection(rng, length as usize, amount as usize) + sample_rejection(rng, length, amount) } } } @@ -227,8 +244,7 @@ pub fn sample<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec /// /// This implementation uses `O(amount)` memory and `O(amount^2)` time. fn sample_floyd<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec - where R: Rng + ?Sized, -{ +where R: Rng + ?Sized { // For small amount we use Floyd's fully-shuffled variant. For larger // amounts this is slow due to Vec::insert performance, so we shuffle // afterwards. Benchmarks show little overhead from extra logic. @@ -243,11 +259,9 @@ fn sample_floyd<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec indices.insert(pos, j); continue; } - } else { - if indices.contains(&t) { - indices.push(j); - continue; - } + } else if indices.contains(&t) { + indices.push(j); + continue; } indices.push(t); } @@ -274,8 +288,7 @@ fn sample_floyd<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec /// /// Set-up is `O(length)` time and memory and shuffling is `O(amount)` time. fn sample_inplace<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec - where R: Rng + ?Sized, -{ +where R: Rng + ?Sized { debug_assert!(amount <= length); let mut indices: Vec<u32> = Vec::with_capacity(length as usize); indices.extend(0..length); @@ -288,21 +301,36 @@ fn sample_inplace<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec IndexVec::from(indices) } +trait UInt: Copy + PartialOrd + Ord + PartialEq + Eq + SampleUniform + core::hash::Hash { + fn zero() -> Self; + fn as_usize(self) -> usize; +} +impl UInt for u32 { + #[inline] fn zero() -> Self { 0 } + #[inline] fn as_usize(self) -> usize { self as usize } +} +impl UInt for usize { + #[inline] fn zero() -> Self { 0 } + #[inline] fn as_usize(self) -> usize { self } +} + /// Randomly sample exactly `amount` indices from `0..length`, using rejection /// sampling. -/// +/// /// Since `amount <<< length` there is a low chance of a random sample in /// `0..length` being a duplicate. We test for duplicates and resample where /// necessary. The algorithm is `O(amount)` time and memory. -fn sample_rejection<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec - where R: Rng + ?Sized, -{ +/// +/// This function is generic over X primarily so that results are value-stable +/// over 32-bit and 64-bit platforms. +fn sample_rejection<X: UInt, R>(rng: &mut R, length: X, amount: X) -> IndexVec +where R: Rng + ?Sized, IndexVec: From<Vec<X>> { debug_assert!(amount < length); - #[cfg(feature="std")] let mut cache = HashSet::with_capacity(amount); + #[cfg(feature="std")] let mut cache = HashSet::with_capacity(amount.as_usize()); #[cfg(not(feature="std"))] let mut cache = BTreeSet::new(); - let distr = Uniform::new(0, length); - let mut indices = Vec::with_capacity(amount); - for _ in 0..amount { + let distr = Uniform::new(X::zero(), length); + let mut indices = Vec::with_capacity(amount.as_usize()); + for _ in 0..amount.as_usize() { let mut pos = distr.sample(rng); while !cache.insert(pos) { pos = distr.sample(rng); @@ -310,30 +338,32 @@ fn sample_rejection<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec indices.push(pos); } - debug_assert_eq!(indices.len(), amount); + debug_assert_eq!(indices.len(), amount.as_usize()); IndexVec::from(indices) } #[cfg(test)] mod test { + #[cfg(feature="std")] use std::vec; + #[cfg(all(feature="alloc", not(feature="std")))] use crate::alloc::vec; use super::*; #[test] fn test_sample_boundaries() { - let mut r = ::test::rng(404); + let mut r = crate::test::rng(404); assert_eq!(sample_inplace(&mut r, 0, 0).len(), 0); assert_eq!(sample_inplace(&mut r, 1, 0).len(), 0); assert_eq!(sample_inplace(&mut r, 1, 1).into_vec(), vec![0]); - assert_eq!(sample_rejection(&mut r, 1, 0).len(), 0); + assert_eq!(sample_rejection(&mut r, 1u32, 0).len(), 0); assert_eq!(sample_floyd(&mut r, 0, 0).len(), 0); assert_eq!(sample_floyd(&mut r, 1, 0).len(), 0); assert_eq!(sample_floyd(&mut r, 1, 1).into_vec(), vec![0]); // These algorithms should be fast with big numbers. Test average. - let sum: usize = sample_rejection(&mut r, 1 << 25, 10) + let sum: usize = sample_rejection(&mut r, 1 << 25, 10u32) .into_iter().sum(); assert!(1 << 25 < sum && sum < (1 << 25) * 25); @@ -343,8 +373,9 @@ mod test { } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_sample_alg() { - let seed_rng = ::test::rng; + let seed_rng = crate::test::rng; // We can't test which algorithm is used directly, but Floyd's alg // should produce different results from the others. (Also, `inplace` @@ -371,7 +402,7 @@ mod test { // A large length and larger amount should use cache let (length, amount): (usize, usize) = (1<<20, 600); let v1 = sample(&mut seed_rng(422), length, amount); - let v2 = sample_rejection(&mut seed_rng(422), length, amount); + let v2 = sample_rejection(&mut seed_rng(422), length as u32, amount as u32); assert!(v1.iter().all(|e| e < length)); assert_eq!(v1, v2); } diff --git a/rand/src/seq/mod.rs b/rand/src/seq/mod.rs index 9959602..cec9bb1 100644 --- a/rand/src/seq/mod.rs +++ b/rand/src/seq/mod.rs @@ -6,25 +6,55 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Functions for randomly accessing and sampling sequences. +//! Sequence-related functionality //! -//! TODO: module doc +//! This module provides: +//! +//! * [`seq::SliceRandom`] slice sampling and mutation +//! * [`seq::IteratorRandom`] iterator sampling +//! * [`seq::index::sample`] low-level API to choose multiple indices from +//! `0..length` +//! +//! Also see: +//! +//! * [`distributions::weighted`] module which provides implementations of +//! weighted index sampling. +//! +//! In order to make results reproducible across 32-64 bit architectures, all +//! `usize` indices are sampled as a `u32` where possible (also providing a +//! small performance boost in some cases). #[cfg(feature="alloc")] pub mod index; #[cfg(feature="alloc")] use core::ops::Index; -#[cfg(all(feature="alloc", not(feature="std")))] use alloc::vec::Vec; +#[cfg(all(feature="alloc", not(feature="std")))] use crate::alloc::vec::Vec; -use Rng; -#[cfg(feature="alloc")] use distributions::WeightedError; -#[cfg(feature="alloc")] use distributions::uniform::{SampleUniform, SampleBorrow}; +use crate::Rng; +#[cfg(feature="alloc")] use crate::distributions::WeightedError; +#[cfg(feature="alloc")] use crate::distributions::uniform::{SampleUniform, SampleBorrow}; /// Extension trait on slices, providing random mutation and sampling methods. /// -/// An implementation is provided for slices. This may also be implementable for -/// other types. +/// This trait is implemented on all `[T]` slice types, providing several +/// methods for choosing and shuffling elements. You must `use` this trait: +/// +/// ``` +/// use rand::seq::SliceRandom; +/// +/// fn main() { +/// let mut rng = rand::thread_rng(); +/// let mut bytes = "Hello, random!".to_string().into_bytes(); +/// bytes.shuffle(&mut rng); +/// let str = String::from_utf8(bytes).unwrap(); +/// println!("{}", str); +/// } +/// ``` +/// Example output (non-deterministic): +/// ```none +/// l,nmroHado !le +/// ``` pub trait SliceRandom { /// The element type. type Item; @@ -32,7 +62,7 @@ pub trait SliceRandom { /// Returns a reference to one random element of the slice, or `None` if the /// slice is empty. /// - /// Depending on the implementation, complexity is expected to be `O(1)`. + /// For slices, complexity is `O(1)`. /// /// # Example /// @@ -46,33 +76,33 @@ pub trait SliceRandom { /// assert_eq!(choices[..0].choose(&mut rng), None); /// ``` fn choose<R>(&self, rng: &mut R) -> Option<&Self::Item> - where R: Rng + ?Sized; + where R: Rng + ?Sized; /// Returns a mutable reference to one random element of the slice, or /// `None` if the slice is empty. - /// - /// Depending on the implementation, complexity is expected to be `O(1)`. + /// + /// For slices, complexity is `O(1)`. fn choose_mut<R>(&mut self, rng: &mut R) -> Option<&mut Self::Item> - where R: Rng + ?Sized; + where R: Rng + ?Sized; - /// Produces an iterator that chooses `amount` elements from the slice at - /// random without repeating any, and returns them in random order. - /// - /// In case this API is not sufficiently flexible, use `index::sample` then - /// apply the indices to the slice. - /// - /// Complexity is expected to be the same as `index::sample`. - /// + /// Chooses `amount` elements from the slice at random, without repetition, + /// and in random order. The returned iterator is appropriate both for + /// collection into a `Vec` and filling an existing buffer (see example). + /// + /// In case this API is not sufficiently flexible, use [`index::sample`]. + /// + /// For slices, complexity is the same as [`index::sample`]. + /// /// # Example /// ``` /// use rand::seq::SliceRandom; - /// + /// /// let mut rng = &mut rand::thread_rng(); /// let sample = "Hello, audience!".as_bytes(); - /// + /// /// // collect the results into a vector: /// let v: Vec<u8> = sample.choose_multiple(&mut rng, 3).cloned().collect(); - /// + /// /// // store in a buffer: /// let mut buf = [0u8; 5]; /// for (b, slot) in sample.choose_multiple(&mut rng, buf.len()).zip(buf.iter_mut()) { @@ -81,13 +111,18 @@ pub trait SliceRandom { /// ``` #[cfg(feature = "alloc")] fn choose_multiple<R>(&self, rng: &mut R, amount: usize) -> SliceChooseIter<Self, Self::Item> - where R: Rng + ?Sized; + where R: Rng + ?Sized; - /// Similar to [`choose`], where the likelihood of each outcome may be - /// specified. The specified function `weight` maps items `x` to a relative + /// Similar to [`choose`], but where the likelihood of each outcome may be + /// specified. + /// + /// The specified function `weight` maps each item `x` to a relative /// likelihood `weight(x)`. The probability of each item being selected is /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`. /// + /// For slices of length `n`, complexity is `O(n)`. + /// See also [`choose_weighted_mut`], [`distributions::weighted`]. + /// /// # Example /// /// ``` @@ -98,47 +133,59 @@ pub trait SliceRandom { /// // 50% chance to print 'a', 25% chance to print 'b', 25% chance to print 'c' /// println!("{:?}", choices.choose_weighted(&mut rng, |item| item.1).unwrap().0); /// ``` - /// [`choose`]: trait.SliceRandom.html#method.choose + /// [`choose`]: SliceRandom::choose + /// [`choose_weighted_mut`]: SliceRandom::choose_weighted_mut + /// [`distributions::weighted`]: crate::distributions::weighted #[cfg(feature = "alloc")] - fn choose_weighted<R, F, B, X>(&self, rng: &mut R, weight: F) -> Result<&Self::Item, WeightedError> - where R: Rng + ?Sized, - F: Fn(&Self::Item) -> B, - B: SampleBorrow<X>, - X: SampleUniform + - for<'a> ::core::ops::AddAssign<&'a X> + - ::core::cmp::PartialOrd<X> + - Clone + - Default; - - /// Similar to [`choose_mut`], where the likelihood of each outcome may be - /// specified. The specified function `weight` maps items `x` to a relative + fn choose_weighted<R, F, B, X>( + &self, rng: &mut R, weight: F, + ) -> Result<&Self::Item, WeightedError> + where + R: Rng + ?Sized, + F: Fn(&Self::Item) -> B, + B: SampleBorrow<X>, + X: SampleUniform + + for<'a> ::core::ops::AddAssign<&'a X> + + ::core::cmp::PartialOrd<X> + + Clone + + Default; + + /// Similar to [`choose_mut`], but where the likelihood of each outcome may + /// be specified. + /// + /// The specified function `weight` maps each item `x` to a relative /// likelihood `weight(x)`. The probability of each item being selected is /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`. /// - /// See also [`choose_weighted`]. + /// For slices of length `n`, complexity is `O(n)`. + /// See also [`choose_weighted`], [`distributions::weighted`]. /// - /// [`choose_mut`]: trait.SliceRandom.html#method.choose_mut - /// [`choose_weighted`]: trait.SliceRandom.html#method.choose_weighted + /// [`choose_mut`]: SliceRandom::choose_mut + /// [`choose_weighted`]: SliceRandom::choose_weighted + /// [`distributions::weighted`]: crate::distributions::weighted #[cfg(feature = "alloc")] - fn choose_weighted_mut<R, F, B, X>(&mut self, rng: &mut R, weight: F) -> Result<&mut Self::Item, WeightedError> - where R: Rng + ?Sized, - F: Fn(&Self::Item) -> B, - B: SampleBorrow<X>, - X: SampleUniform + - for<'a> ::core::ops::AddAssign<&'a X> + - ::core::cmp::PartialOrd<X> + - Clone + - Default; + fn choose_weighted_mut<R, F, B, X>( + &mut self, rng: &mut R, weight: F, + ) -> Result<&mut Self::Item, WeightedError> + where + R: Rng + ?Sized, + F: Fn(&Self::Item) -> B, + B: SampleBorrow<X>, + X: SampleUniform + + for<'a> ::core::ops::AddAssign<&'a X> + + ::core::cmp::PartialOrd<X> + + Clone + + Default; /// Shuffle a mutable slice in place. - /// - /// Depending on the implementation, complexity is expected to be `O(1)`. + /// + /// For slices of length `n`, complexity is `O(n)`. /// /// # Example /// /// ``` - /// use rand::thread_rng; /// use rand::seq::SliceRandom; + /// use rand::thread_rng; /// /// let mut rng = thread_rng(); /// let mut y = [1, 2, 3, 4, 5]; @@ -146,7 +193,8 @@ pub trait SliceRandom { /// y.shuffle(&mut rng); /// println!("Shuffled: {:?}", y); /// ``` - fn shuffle<R>(&mut self, rng: &mut R) where R: Rng + ?Sized; + fn shuffle<R>(&mut self, rng: &mut R) + where R: Rng + ?Sized; /// Shuffle a slice in place, but exit early. /// @@ -164,47 +212,65 @@ pub trait SliceRandom { /// If `amount` is greater than the number of elements in the slice, this /// will perform a full shuffle. /// - /// Complexity is expected to be `O(m)` where `m = amount`. - fn partial_shuffle<R>(&mut self, rng: &mut R, amount: usize) - -> (&mut [Self::Item], &mut [Self::Item]) where R: Rng + ?Sized; + /// For slices, complexity is `O(m)` where `m = amount`. + fn partial_shuffle<R>( + &mut self, rng: &mut R, amount: usize, + ) -> (&mut [Self::Item], &mut [Self::Item]) + where R: Rng + ?Sized; } /// Extension trait on iterators, providing random sampling methods. +/// +/// This trait is implemented on all sized iterators, providing methods for +/// choosing one or more elements. You must `use` this trait: +/// +/// ``` +/// use rand::seq::IteratorRandom; +/// +/// fn main() { +/// let mut rng = rand::thread_rng(); +/// +/// let faces = "😀😎😐😕😠😢"; +/// println!("I am {}!", faces.chars().choose(&mut rng).unwrap()); +/// } +/// ``` +/// Example output (non-deterministic): +/// ```none +/// I am 😀! +/// ``` pub trait IteratorRandom: Iterator + Sized { - /// Choose one element at random from the iterator. If you have a slice, - /// it's significantly faster to call the [`choose`] or [`choose_mut`] - /// functions using the slice instead. - /// - /// Returns `None` if and only if the iterator is empty. + /// Choose one element at random from the iterator. /// - /// Complexity is `O(n)`, where `n` is the length of the iterator. - /// This likely consumes multiple random numbers, but the exact number - /// is unspecified. + /// Returns `None` if and only if the iterator is empty. /// - /// [`choose`]: trait.SliceRandom.html#method.choose - /// [`choose_mut`]: trait.SliceRandom.html#method.choose_mut + /// This method uses [`Iterator::size_hint`] for optimisation. With an + /// accurate hint and where [`Iterator::nth`] is a constant-time operation + /// this method can offer `O(1)` performance. Where no size hint is + /// available, complexity is `O(n)` where `n` is the iterator length. + /// Partial hints (where `lower > 0`) also improve performance. + /// + /// For slices, prefer [`SliceRandom::choose`] which guarantees `O(1)` + /// performance. fn choose<R>(mut self, rng: &mut R) -> Option<Self::Item> - where R: Rng + ?Sized - { + where R: Rng + ?Sized { let (mut lower, mut upper) = self.size_hint(); let mut consumed = 0; let mut result = None; if upper == Some(lower) { - return if lower == 0 { None } else { self.nth(rng.gen_range(0, lower)) }; + return if lower == 0 { None } else { self.nth(gen_index(rng, lower)) }; } // Continue until the iterator is exhausted loop { if lower > 1 { - let ix = rng.gen_range(0, lower + consumed); - let skip; - if ix < lower { + let ix = gen_index(rng, lower + consumed); + let skip = if ix < lower { result = self.nth(ix); - skip = lower - (ix + 1); + lower - (ix + 1) } else { - skip = lower; - } + lower + }; if upper == Some(lower) { return result; } @@ -230,21 +296,21 @@ pub trait IteratorRandom: Iterator + Sized { } } - /// Collects `amount` values at random from the iterator into a supplied - /// buffer. - /// + /// Collects values at random from the iterator into a supplied buffer + /// until that buffer is filled. + /// /// Although the elements are selected randomly, the order of elements in /// the buffer is neither stable nor fully random. If random ordering is /// desired, shuffle the result. - /// - /// Returns the number of elements added to the buffer. This equals `amount` - /// unless the iterator contains insufficient elements, in which case this - /// equals the number of elements available. - /// + /// + /// Returns the number of elements added to the buffer. This equals the length + /// of the buffer unless the iterator contains insufficient elements, in which + /// case this equals the number of elements available. + /// /// Complexity is `O(n)` where `n` is the length of the iterator. - fn choose_multiple_fill<R>(mut self, rng: &mut R, buf: &mut [Self::Item]) - -> usize where R: Rng + ?Sized - { + /// For slices, prefer [`SliceRandom::choose_multiple`]. + fn choose_multiple_fill<R>(mut self, rng: &mut R, buf: &mut [Self::Item]) -> usize + where R: Rng + ?Sized { let amount = buf.len(); let mut len = 0; while len < amount { @@ -259,7 +325,7 @@ pub trait IteratorRandom: Iterator + Sized { // Continue, since the iterator was not exhausted for (i, elem) in self.enumerate() { - let k = rng.gen_range(0, i + 1 + amount); + let k = gen_index(rng, i + 1 + amount); if let Some(slot) = buf.get_mut(k) { *slot = elem; } @@ -274,16 +340,16 @@ pub trait IteratorRandom: Iterator + Sized { /// Although the elements are selected randomly, the order of elements in /// the buffer is neither stable nor fully random. If random ordering is /// desired, shuffle the result. - /// + /// /// The length of the returned vector equals `amount` unless the iterator /// contains insufficient elements, in which case it equals the number of /// elements available. - /// + /// /// Complexity is `O(n)` where `n` is the length of the iterator. + /// For slices, prefer [`SliceRandom::choose_multiple`]. #[cfg(feature = "alloc")] fn choose_multiple<R>(mut self, rng: &mut R, amount: usize) -> Vec<Self::Item> - where R: Rng + ?Sized - { + where R: Rng + ?Sized { let mut reservoir = Vec::with_capacity(amount); reservoir.extend(self.by_ref().take(amount)); @@ -293,7 +359,7 @@ pub trait IteratorRandom: Iterator + Sized { // If the iterator stops once, then so do we. if reservoir.len() == amount { for (i, elem) in self.enumerate() { - let k = rng.gen_range(0, i + 1 + amount); + let k = gen_index(rng, i + 1 + amount); if let Some(slot) = reservoir.get_mut(k) { *slot = elem; } @@ -312,31 +378,27 @@ impl<T> SliceRandom for [T] { type Item = T; fn choose<R>(&self, rng: &mut R) -> Option<&Self::Item> - where R: Rng + ?Sized - { + where R: Rng + ?Sized { if self.is_empty() { None } else { - Some(&self[rng.gen_range(0, self.len())]) + Some(&self[gen_index(rng, self.len())]) } } fn choose_mut<R>(&mut self, rng: &mut R) -> Option<&mut Self::Item> - where R: Rng + ?Sized - { + where R: Rng + ?Sized { if self.is_empty() { None } else { let len = self.len(); - Some(&mut self[rng.gen_range(0, len)]) + Some(&mut self[gen_index(rng, len)]) } } #[cfg(feature = "alloc")] - fn choose_multiple<R>(&self, rng: &mut R, amount: usize) - -> SliceChooseIter<Self, Self::Item> - where R: Rng + ?Sized - { + fn choose_multiple<R>(&self, rng: &mut R, amount: usize) -> SliceChooseIter<Self, Self::Item> + where R: Rng + ?Sized { let amount = ::core::cmp::min(amount, self.len()); SliceChooseIter { slice: self, @@ -346,57 +408,66 @@ impl<T> SliceRandom for [T] { } #[cfg(feature = "alloc")] - fn choose_weighted<R, F, B, X>(&self, rng: &mut R, weight: F) -> Result<&Self::Item, WeightedError> - where R: Rng + ?Sized, - F: Fn(&Self::Item) -> B, - B: SampleBorrow<X>, - X: SampleUniform + - for<'a> ::core::ops::AddAssign<&'a X> + - ::core::cmp::PartialOrd<X> + - Clone + - Default { - use distributions::{Distribution, WeightedIndex}; + fn choose_weighted<R, F, B, X>( + &self, rng: &mut R, weight: F, + ) -> Result<&Self::Item, WeightedError> + where + R: Rng + ?Sized, + F: Fn(&Self::Item) -> B, + B: SampleBorrow<X>, + X: SampleUniform + + for<'a> ::core::ops::AddAssign<&'a X> + + ::core::cmp::PartialOrd<X> + + Clone + + Default, + { + use crate::distributions::{Distribution, WeightedIndex}; let distr = WeightedIndex::new(self.iter().map(weight))?; Ok(&self[distr.sample(rng)]) } #[cfg(feature = "alloc")] - fn choose_weighted_mut<R, F, B, X>(&mut self, rng: &mut R, weight: F) -> Result<&mut Self::Item, WeightedError> - where R: Rng + ?Sized, - F: Fn(&Self::Item) -> B, - B: SampleBorrow<X>, - X: SampleUniform + - for<'a> ::core::ops::AddAssign<&'a X> + - ::core::cmp::PartialOrd<X> + - Clone + - Default { - use distributions::{Distribution, WeightedIndex}; + fn choose_weighted_mut<R, F, B, X>( + &mut self, rng: &mut R, weight: F, + ) -> Result<&mut Self::Item, WeightedError> + where + R: Rng + ?Sized, + F: Fn(&Self::Item) -> B, + B: SampleBorrow<X>, + X: SampleUniform + + for<'a> ::core::ops::AddAssign<&'a X> + + ::core::cmp::PartialOrd<X> + + Clone + + Default, + { + use crate::distributions::{Distribution, WeightedIndex}; let distr = WeightedIndex::new(self.iter().map(weight))?; Ok(&mut self[distr.sample(rng)]) } - fn shuffle<R>(&mut self, rng: &mut R) where R: Rng + ?Sized - { + fn shuffle<R>(&mut self, rng: &mut R) + where R: Rng + ?Sized { for i in (1..self.len()).rev() { // invariant: elements with index > i have been locked in place. - self.swap(i, rng.gen_range(0, i + 1)); + self.swap(i, gen_index(rng, i + 1)); } } - fn partial_shuffle<R>(&mut self, rng: &mut R, amount: usize) - -> (&mut [Self::Item], &mut [Self::Item]) where R: Rng + ?Sized - { + fn partial_shuffle<R>( + &mut self, rng: &mut R, amount: usize, + ) -> (&mut [Self::Item], &mut [Self::Item]) + where R: Rng + ?Sized { // This applies Durstenfeld's algorithm for the // [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) // for an unbiased permutation, but exits early after choosing `amount` // elements. - + let len = self.len(); let end = if amount >= len { 0 } else { len - amount }; - + for i in (end..len).rev() { // invariant: elements with index > i have been locked in place. - self.swap(i, rng.gen_range(0, i + 1)); + self.swap(i, gen_index(rng, i + 1)); } let r = self.split_at_mut(end); (r.1, r.0) @@ -406,8 +477,10 @@ impl<T> SliceRandom for [T] { impl<I> IteratorRandom for I where I: Iterator + Sized {} -/// Iterator over multiple choices, as returned by [`SliceRandom::choose_multiple]( -/// trait.SliceRandom.html#method.choose_multiple). +/// An iterator over multiple slice elements. +/// +/// This struct is created by +/// [`SliceRandom::choose_multiple`](trait.SliceRandom.html#tymethod.choose_multiple). #[cfg(feature = "alloc")] #[derive(Debug)] pub struct SliceChooseIter<'a, S: ?Sized + 'a, T: 'a> { @@ -424,7 +497,7 @@ impl<'a, S: Index<usize, Output = T> + ?Sized + 'a, T: 'a> Iterator for SliceCho // TODO: investigate using SliceIndex::get_unchecked when stable self.indices.next().map(|i| &self.slice[i as usize]) } - + fn size_hint(&self) -> (usize, Option<usize>) { (self.indices.len(), Some(self.indices.len())) } @@ -440,94 +513,39 @@ impl<'a, S: Index<usize, Output = T> + ?Sized + 'a, T: 'a> ExactSizeIterator } -/// Randomly sample `amount` elements from a finite iterator. -/// -/// Deprecated: use [`IteratorRandom::choose_multiple`] instead. -/// -/// [`IteratorRandom::choose_multiple`]: trait.IteratorRandom.html#method.choose_multiple -#[cfg(feature = "alloc")] -#[deprecated(since="0.6.0", note="use IteratorRandom::choose_multiple instead")] -pub fn sample_iter<T, I, R>(rng: &mut R, iterable: I, amount: usize) -> Result<Vec<T>, Vec<T>> - where I: IntoIterator<Item=T>, - R: Rng + ?Sized, -{ - use seq::IteratorRandom; - let iter = iterable.into_iter(); - let result = iter.choose_multiple(rng, amount); - if result.len() == amount { - Ok(result) +// Sample a number uniformly between 0 and `ubound`. Uses 32-bit sampling where +// possible, primarily in order to produce the same output on 32-bit and 64-bit +// platforms. +#[inline] +fn gen_index<R: Rng + ?Sized>(rng: &mut R, ubound: usize) -> usize { + if ubound <= (core::u32::MAX as usize) { + rng.gen_range(0, ubound as u32) as usize } else { - Err(result) + rng.gen_range(0, ubound) } } -/// Randomly sample exactly `amount` values from `slice`. -/// -/// The values are non-repeating and in random order. -/// -/// This implementation uses `O(amount)` time and memory. -/// -/// Panics if `amount > slice.len()` -/// -/// Deprecated: use [`SliceRandom::choose_multiple`] instead. -/// -/// [`SliceRandom::choose_multiple`]: trait.SliceRandom.html#method.choose_multiple -#[cfg(feature = "alloc")] -#[deprecated(since="0.6.0", note="use SliceRandom::choose_multiple instead")] -pub fn sample_slice<R, T>(rng: &mut R, slice: &[T], amount: usize) -> Vec<T> - where R: Rng + ?Sized, - T: Clone -{ - let indices = index::sample(rng, slice.len(), amount).into_iter(); - - let mut out = Vec::with_capacity(amount); - out.extend(indices.map(|i| slice[i].clone())); - out -} - -/// Randomly sample exactly `amount` references from `slice`. -/// -/// The references are non-repeating and in random order. -/// -/// This implementation uses `O(amount)` time and memory. -/// -/// Panics if `amount > slice.len()` -/// -/// Deprecated: use [`SliceRandom::choose_multiple`] instead. -/// -/// [`SliceRandom::choose_multiple`]: trait.SliceRandom.html#method.choose_multiple -#[cfg(feature = "alloc")] -#[deprecated(since="0.6.0", note="use SliceRandom::choose_multiple instead")] -pub fn sample_slice_ref<'a, R, T>(rng: &mut R, slice: &'a [T], amount: usize) -> Vec<&'a T> - where R: Rng + ?Sized -{ - let indices = index::sample(rng, slice.len(), amount).into_iter(); - - let mut out = Vec::with_capacity(amount); - out.extend(indices.map(|i| &slice[i])); - out -} #[cfg(test)] mod test { use super::*; - #[cfg(feature = "alloc")] use {Rng, SeedableRng}; - #[cfg(feature = "alloc")] use rngs::SmallRng; + #[cfg(feature = "alloc")] use crate::Rng; #[cfg(all(feature="alloc", not(feature="std")))] use alloc::vec::Vec; #[test] fn test_slice_choose() { - let mut r = ::test::rng(107); + let mut r = crate::test::rng(107); let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n']; let mut chosen = [0i32; 14]; + // The below all use a binomial distribution with n=1000, p=1/14. + // binocdf(40, 1000, 1/14) ~= 2e-5; 1-binocdf(106, ..) ~= 2e-5 for _ in 0..1000 { let picked = *chars.choose(&mut r).unwrap(); chosen[(picked as usize) - ('a' as usize)] += 1; } for count in chosen.iter() { - let err = *count - (1000 / (chars.len() as i32)); - assert!(-20 <= err && err <= 20); + assert!(40 < *count && *count < 106); } chosen.iter_mut().for_each(|x| *x = 0); @@ -535,8 +553,7 @@ mod test { *chosen.choose_mut(&mut r).unwrap() += 1; } for count in chosen.iter() { - let err = *count - (1000 / (chosen.len() as i32)); - assert!(-20 <= err && err <= 20); + assert!(40 < *count && *count < 106); } let mut v: [isize; 0] = []; @@ -597,8 +614,9 @@ mod test { } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_iterator_choose() { - let r = &mut ::test::rng(109); + let r = &mut crate::test::rng(109); fn test_iter<R: Rng + ?Sized, Iter: Iterator<Item=usize> + Clone>(r: &mut R, iter: Iter) { let mut chosen = [0i32; 9]; for _ in 0..1000 { @@ -628,8 +646,9 @@ mod test { } #[test] + #[cfg(not(miri))] // Miri is too slow fn test_shuffle() { - let mut r = ::test::rng(108); + let mut r = crate::test::rng(108); let empty: &mut [isize] = &mut []; empty.shuffle(&mut r); let mut one = [1]; @@ -669,14 +688,16 @@ mod test { counts[permutation] += 1; } for count in counts.iter() { - let err = *count - 10000i32 / 24; - assert!(-50 <= err && err <= 50); + // Binomial(10000, 1/24) with average 416.667 + // Octave: binocdf(n, 10000, 1/24) + // 99.9% chance samples lie within this range: + assert!(352 <= *count && *count <= 483, "count: {}", count); } } #[test] fn test_partial_shuffle() { - let mut r = ::test::rng(118); + let mut r = crate::test::rng(118); let mut empty: [u32; 0] = []; let res = empty.partial_shuffle(&mut r, 10); @@ -696,7 +717,7 @@ mod test { let min_val = 1; let max_val = 100; - let mut r = ::test::rng(401); + let mut r = crate::test::rng(401); let vals = (min_val..max_val).collect::<Vec<i32>>(); let small_sample = vals.iter().choose_multiple(&mut r, 5); let large_sample = vals.iter().choose_multiple(&mut r, vals.len() + 5); @@ -713,75 +734,9 @@ mod test { #[test] #[cfg(feature = "alloc")] - #[allow(deprecated)] - fn test_sample_slice_boundaries() { - let empty: &[u8] = &[]; - - let mut r = ::test::rng(402); - - // sample 0 items - assert_eq!(&sample_slice(&mut r, empty, 0)[..], [0u8; 0]); - assert_eq!(&sample_slice(&mut r, &[42, 2, 42], 0)[..], [0u8; 0]); - - // sample 1 item - assert_eq!(&sample_slice(&mut r, &[42], 1)[..], [42]); - let v = sample_slice(&mut r, &[1, 42], 1)[0]; - assert!(v == 1 || v == 42); - - // sample "all" the items - let v = sample_slice(&mut r, &[42, 133], 2); - assert!(&v[..] == [42, 133] || v[..] == [133, 42]); - - // Make sure lucky 777's aren't lucky - let slice = &[42, 777]; - let mut num_42 = 0; - let total = 1000; - for _ in 0..total { - let v = sample_slice(&mut r, slice, 1); - assert_eq!(v.len(), 1); - let v = v[0]; - assert!(v == 42 || v == 777); - if v == 42 { - num_42 += 1; - } - } - let ratio_42 = num_42 as f64 / 1000 as f64; - assert!(0.4 <= ratio_42 || ratio_42 <= 0.6, "{}", ratio_42); - } - - #[test] - #[cfg(feature = "alloc")] - #[allow(deprecated)] - fn test_sample_slice() { - let seeded_rng = SmallRng::from_seed; - - let mut r = ::test::rng(403); - - for n in 1..20 { - let length = 5*n - 4; // 1, 6, ... - let amount = r.gen_range(0, length); - let mut seed = [0u8; 16]; - r.fill(&mut seed); - - // assert the basics work - let regular = index::sample(&mut seeded_rng(seed), length, amount); - assert_eq!(regular.len(), amount); - assert!(regular.iter().all(|e| e < length)); - - // also test that sampling the slice works - let vec: Vec<u32> = (0..(length as u32)).collect(); - let result = sample_slice(&mut seeded_rng(seed), &vec, amount); - assert_eq!(result, regular.iter().map(|i| i as u32).collect::<Vec<_>>()); - - let result = sample_slice_ref(&mut seeded_rng(seed), &vec, amount); - assert!(result.iter().zip(regular.iter()).all(|(i,j)| **i == j as u32)); - } - } - - #[test] - #[cfg(feature = "alloc")] + #[cfg(not(miri))] // Miri is too slow fn test_weighted() { - let mut r = ::test::rng(406); + let mut r = crate::test::rng(406); const N_REPS: u32 = 3000; let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]; let total_weight = weights.iter().sum::<u32>() as f32; @@ -830,7 +785,7 @@ mod test { assert_eq!(empty_slice.choose_weighted(&mut r, |_| 1), Err(WeightedError::NoItem)); assert_eq!(empty_slice.choose_weighted_mut(&mut r, |_| 1), Err(WeightedError::NoItem)); assert_eq!(['x'].choose_weighted_mut(&mut r, |_| 0), Err(WeightedError::AllWeightsZero)); - assert_eq!([0, -1].choose_weighted_mut(&mut r, |x| *x), Err(WeightedError::NegativeWeight)); - assert_eq!([-1, 0].choose_weighted_mut(&mut r, |x| *x), Err(WeightedError::NegativeWeight)); + assert_eq!([0, -1].choose_weighted_mut(&mut r, |x| *x), Err(WeightedError::InvalidWeight)); + assert_eq!([-1, 0].choose_weighted_mut(&mut r, |x| *x), Err(WeightedError::InvalidWeight)); } } diff --git a/rand/tests/wasm_bindgen/Cargo.toml b/rand/tests/wasm_bindgen/Cargo.toml new file mode 100644 index 0000000..e83c174 --- /dev/null +++ b/rand/tests/wasm_bindgen/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rand_wasm_bindgen_test" +description = "Minimal crate to test that rand can be build for web assembly target" +version = "0.1.0" +authors = ["The Rand Project Developers"] +publish = false +license = "MIT OR Apache-2.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +rand = { path = "../..", features = ["wasm-bindgen"] } +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.2" diff --git a/rand/tests/wasm_bindgen/js/index.js b/rand/tests/wasm_bindgen/js/index.js new file mode 100644 index 0000000..a02fb59 --- /dev/null +++ b/rand/tests/wasm_bindgen/js/index.js @@ -0,0 +1,7 @@ +'use strict'; + +const rand_wasm_bindgen_test = require('./rand_wasm_bindgen_test'); + +console.log(rand_wasm_bindgen_test.generate_from_entropy()); +console.log(rand_wasm_bindgen_test.generate_from_os_rand()); +console.log(rand_wasm_bindgen_test.generate_from_seed()); diff --git a/rand/tests/wasm_bindgen/src/lib.rs b/rand/tests/wasm_bindgen/src/lib.rs new file mode 100644 index 0000000..9af0b9e --- /dev/null +++ b/rand/tests/wasm_bindgen/src/lib.rs @@ -0,0 +1,49 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Crate to test WASM with the `wasm-bindgen` lib. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png")] + +use rand::rngs::{OsRng, StdRng}; +use rand::{Rng, SeedableRng}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn generate_from_seed(seed: u32) -> i32 { + StdRng::seed_from_u64(seed as u64).gen() +} + +#[wasm_bindgen] +pub fn generate_from_os_rand() -> i32 { + OsRng.gen() +} + +#[wasm_bindgen] +pub fn generate_from_entropy() -> i32 { + StdRng::from_entropy().gen() +} + +pub mod tests { + use wasm_bindgen_test::*; + + #[wasm_bindgen_test] + fn generate_from_seed() { + let _ = super::generate_from_seed(42); + } + + #[wasm_bindgen_test] + fn generate_from_os_rand() { + let _ = super::generate_from_os_rand(); + } + + #[wasm_bindgen_test] + fn generate_from_entropy() { + let _ = super::generate_from_entropy(); + } +} diff --git a/rand/utils/ci/miri.sh b/rand/utils/ci/miri.sh new file mode 100644 index 0000000..209adf2 --- /dev/null +++ b/rand/utils/ci/miri.sh @@ -0,0 +1,23 @@ +set -ex + +MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) +echo "Installing latest nightly with Miri: $MIRI_NIGHTLY" +rustup default "$MIRI_NIGHTLY" + +rustup component add miri +cargo miri setup + +cargo miri test --no-default-features -- -- -Zunstable-options --exclude-should-panic +cargo miri test --features=log -- -- -Zunstable-options --exclude-should-panic +cargo miri test --manifest-path rand_core/Cargo.toml +cargo miri test --manifest-path rand_core/Cargo.toml --features=serde1 +cargo miri test --manifest-path rand_core/Cargo.toml --no-default-features +#cargo miri test --manifest-path rand_distr/Cargo.toml # no unsafe and lots of slow tests +cargo miri test --manifest-path rand_isaac/Cargo.toml --features=serde1 +cargo miri test --manifest-path rand_pcg/Cargo.toml --features=serde1 +cargo miri test --manifest-path rand_xorshift/Cargo.toml --features=serde1 +cargo miri test --manifest-path rand_xoshiro/Cargo.toml --features=serde1 +cargo miri test --manifest-path rand_chacha/Cargo.toml --no-default-features +cargo miri test --manifest-path rand_hc/Cargo.toml +cargo miri test --manifest-path rand_jitter/Cargo.toml +cargo miri test --manifest-path rand_os/Cargo.toml diff --git a/rand/utils/ci/script.sh b/rand/utils/ci/script.sh index e8c1189..852a850 100644 --- a/rand/utils/ci/script.sh +++ b/rand/utils/ci/script.sh @@ -3,19 +3,22 @@ set -ex main() { - cross test --target $TARGET --lib --no-default-features + cross test --target $TARGET --tests --no-default-features # TODO: add simd_support feature: - cross test --target $TARGET --features=serde1,log + cross test --target $TARGET --features=log cross test --target $TARGET --examples cross test --target $TARGET --manifest-path rand_core/Cargo.toml + cross test --target $TARGET --manifest-path rand_core/Cargo.toml --features=serde1 cross test --target $TARGET --manifest-path rand_core/Cargo.toml --no-default-features + cross test --target $TARGET --manifest-path rand_distr/Cargo.toml cross test --target $TARGET --manifest-path rand_isaac/Cargo.toml --features=serde1 cross test --target $TARGET --manifest-path rand_pcg/Cargo.toml --features=serde1 cross test --target $TARGET --manifest-path rand_xorshift/Cargo.toml --features=serde1 - cross test --target $TARGET --manifest-path rand_xoshiro/Cargo.toml + cross test --target $TARGET --manifest-path rand_xoshiro/Cargo.toml --features=serde1 cross test --target $TARGET --manifest-path rand_chacha/Cargo.toml cross test --target $TARGET --manifest-path rand_hc/Cargo.toml cross test --target $TARGET --manifest-path rand_os/Cargo.toml + cross test --target $TARGET --manifest-path rand_jitter/Cargo.toml } # we don't run the "test phase" when doing deploys |