GithubHelp home page GithubHelp logo

absolucy / nanorand-rs Goto Github PK

View Code? Open in Web Editor NEW
221.0 221.0 23.0 220 KB

A tiny, fast, zero-dep library for random number generation

License: zlib License

Rust 88.78% Python 0.77% C++ 0.31% C 10.14%

nanorand-rs's People

Contributors

0xflotus avatar absolucy avatar figsoda avatar jklott avatar kogia-sima avatar logandark avatar moxinilian avatar n1000 avatar renovate-bot avatar renovate[bot] avatar smvilar avatar tesuji avatar timvisee avatar udoprog avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

nanorand-rs's Issues

impl RandomGen for bool?

rng.generate::<i8>() < 0 works, but having explicit support for random generation of bool might be useful.

generate_range shifts values incorrectly for signed integers

Haven't had a deep look into the code of nanorand-rs yet, but the generate_range seems to incorrectly start the range at 1 less than it should with signed integers.

    let mut rand = WyRand::new();
    // prints: [1, 0, 0, 1, 0, 1, 0, 0, 0, 1]
    println!(
        "{:?}",
        vec![0; 10]
            .into_iter()
            .map(|_| rand.generate_range(1_i32..=2))
            .collect::<Vec<i32>>()
    );
   // Shifting to u32 prints it correctly as: [2, 1, 1, 2, 1, 2, 1, 1, 1, 2]

cannot find function `backup_entropy` in this scope for wasm target even though source seed always provided

Apologies for asking a question by using an issue - I'm new to rust, and I wasn't sure how to deal with this myself.

I am wanting to reliably reproduce/shuffle vast quantities of data in wasm (yew-rs) by way of a seed provided by a server:

let mut rng = WyRand::new_seed(server_seed);
rng.shuffle(&renumber);

I don't think I need an additional source of entropy, since WyRand reproduces the same data for the same seed?

error[E0425]: cannot find function `backup_entropy` in this scope
  --> /home/krolaw/.cargo/registry/src/github.com-1ecc6299db9ec823/nanorand-0.6.1/src/entropy/mod.rs:51:2
   |
51 |     backup_entropy(out);
   |     ^^^^^^^^^^^^^^ not found in this scope

Cargo.toml entry:

nanorand = { version = "0.6.1", features = ["wyrand"] }

Thanks.

Panic on `generate_range`

I'm trying out this lovely library but encountered this panic when calling rng.generate_range<u16>(8192..) on a WyRand RNG:

thread 'protocol::tests::sim_connect_to_peers' panicked at 'attempt to add with overflow', /home/cloudhead/.cargo/registry/src/github.com-1ecc6299db9ec823/nanorand-0.6.1/src/gen.rs:108:1
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/std/src/panicking.rs:515:5
   1: core::panicking::panic_fmt
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/panicking.rs:92:14
   2: core::panicking::panic
             at /rustc/a178d0322ce20e33eac124758e837cbd80a6f633/library/core/src/panicking.rs:50:5
   3: <u16 as nanorand::gen::RandomRange<R>>::random_range
             at /home/cloudhead/.cargo/registry/src/github.com-1ecc6299db9ec823/nanorand-0.6.1/src/gen.rs:50:21
   4: nanorand::rand::Rng::generate_range
             at /home/cloudhead/.cargo/registry/src/github.com-1ecc6299db9ec823/nanorand-0.6.1/src/rand/mod.rs:57:3

Any idea why this is happening? I call the RNG in a lot of places and this seems to be the only thing failing.

0.6 Not compiled on CentOS 7

I have a program depended on nanorand, when upgraded to 0.6 verison, compiling get a link error on CentOS 7.
After downgrade the nanorand verison to 0.5, the compiling is OK.
Perhaps it's because CentOS 7 lacks the getrandom system call.

error: linking with `cc` failed: exit status: 1
  |
  = note: "cc" "-m64" "-Wl,--eh-frame-hdr" "-Wl,-znoexecstack" "-Wl,--as-needed" "-L" "/root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/root/rust-srv/web-connect/target/release/deps/web_connect-af0db03b26adaefa.web_connect.4hmo5qbh-cgu.0.rcgu.o" "-o" "/root/rust-srv/web-connect/target/release/deps/web_connect-af0db03b26adaefa" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro" "-Wl,-znow" "-Wl,-O1" "-nodefaultlibs" "-L" "/root/rust-srv/web-connect/target/release/deps" "-L" "/root/rust-srv/web-connect/target/release/build/brotli-sys-60c0e082e3c3c651/out" "-L" "/root/rust-srv/web-connect/target/release/build/rust-crypto-698176e25e4afa68/out" "-L" "/root/rust-srv/web-connect/target/release/build/jemalloc-sys-1026c0f16ced73f7/out/build/lib" "-L" "/root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/tmp/rustcYFvyx8/libjemalloc_sys-10752eb33d554d19.rlib" "/tmp/rustcYFvyx8/libcrypto-2446112925d5256b.rlib" "/tmp/rustcYFvyx8/libbrotli_sys-3f42712b13ffc0ac.rlib" "-Wl,--start-group" "-Wl,--end-group" "/root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-c4d9a5b072ee3191.rlib" "-Wl,-Bdynamic" "-lpthread" "-lssl" "-lcrypto" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc"
  = note: /root/rust-srv/web-connect/target/release/deps/web_connect-af0db03b26adaefa.web_connect.4hmo5qbh-cgu.0.rcgu.o: In function `_$LT$ntex..ws..stream..StreamEncoder$LT$S$GT$$u20$as$u20$futures_sink..Sink$LT$core..result..Result$LT$ntex..ws..codec..Message$C$E$GT$$GT$$GT$::start_send::ha01b650f1d21cef0':
          web_connect.4hmo5qbh-cgu.0:(.text._ZN143_$LT$ntex..ws..stream..StreamEncoder$LT$S$GT$$u20$as$u20$futures_sink..Sink$LT$core..result..Result$LT$ntex..ws..codec..Message$C$E$GT$$GT$$GT$10start_send17ha01b650f1d21cef0E+0x989): undefined reference to `getrandom'
          web_connect.4hmo5qbh-cgu.0:(.text._ZN143_$LT$ntex..ws..stream..StreamEncoder$LT$S$GT$$u20$as$u20$futures_sink..Sink$LT$core..result..Result$LT$ntex..ws..codec..Message$C$E$GT$$GT$$GT$10start_send17ha01b650f1d21cef0E+0x1111): undefined reference to `getrandom'
          /root/rust-srv/web-connect/target/release/deps/web_connect-af0db03b26adaefa.web_connect.4hmo5qbh-cgu.0.rcgu.o: In function `ntex::ws::frame::Parser::write_message::h32afb0c81fac90ca':
          web_connect.4hmo5qbh-cgu.0:(.text._ZN4ntex2ws5frame6Parser13write_message17h32afb0c81fac90caE+0x35c): undefined reference to `getrandom'
          /root/rust-srv/web-connect/target/release/deps/web_connect-af0db03b26adaefa.web_connect.4hmo5qbh-cgu.0.rcgu.o: In function `ntex::ws::frame::Parser::write_message::haff1caf320801bae':
          web_connect.4hmo5qbh-cgu.0:(.text._ZN4ntex2ws5frame6Parser13write_message17haff1caf320801baeE+0x10c): undefined reference to `getrandom'
          collect2: error: ld returned 1 exit status

Custom initialization of ChaCha counter

Would you be open to a PR? The ChaCha reference allows for initializing the counter to arbitrary values, but the current API doesn't allow this. I expect this would be a fairly simple change to the following code...

/// Initialize the ChaCha internal state, with a 256-bit key and 64-bit nonce.
pub const fn chacha_init(key: [u8; 32], nonce: [u8; 8]) -> [u32; 16] {
	let mut state = [0u32; 16];
	state[0] = chacha_pack(CHACHA_TAU, 0);
	state[1] = chacha_pack(CHACHA_TAU, 4);
	state[2] = chacha_pack(CHACHA_TAU, 8);
	state[3] = chacha_pack(CHACHA_TAU, 12);

	state[4] = chacha_pack(&key, 0);
	state[5] = chacha_pack(&key, 4);
	state[6] = chacha_pack(&key, 8);
	state[7] = chacha_pack(&key, 12);
	state[8] = chacha_pack(&key, 16);
	state[9] = chacha_pack(&key, 20);
	state[10] = chacha_pack(&key, 24);
	state[11] = chacha_pack(&key, 28);

	// 64-bit counter
	state[12] = 0;
	state[13] = 0;
	// Nonce
	state[14] = chacha_pack(&nonce, 0);
	state[15] = chacha_pack(&nonce, 4);
	state
}

Consider not forwarding the getrandom/custom feature

As one the maintainers of getrandom, I would advise against your getrandom_custom feature. Generally speaking, this feature is set by a user who is registering a custom handler (as explained in our docs) and not by a library that just calls getrandom::getrandom. The reason for this is because if a user enables the feature, but does not register a handler, they will get a confusing linker error.

It seems like this feature addition was done to reexport the register_custom_getrandom macro, but a user of nanorand-rs can already just register a handler by just directly depending on getrandom. It should also be noted that directly exporting our macro creates a dependency hazard, as the macro requires a function that returns getrandom::Error. So you would not be able to internally update your version of getrandom (say from 0.2 to 1.0) without having a breaking change yourself. Without this feature, you would be able to update getrandom without needing to release a breaking change in nanorand.

I couldn't find any discussion or PR about why 34ba243 was added. However, it seems like none of the released versions include it yet, so it could be reverted without too much issue.

All the other uses of getrandom's features seem reasonable to me. You could probably unconditionally enable the rdrand feature (even on non-x86 targets) as it has no effect on those targets. Keeping the js feature enabling separate is a good idea due to issues around feature unification and tkaitchuck/aHash#95 (comment)

Add iterator utils

I'm not sure how this would align with nanorand's to-the-point philosophy, but it would be nice to offer the same functionality as https://docs.rs/rand/latest/rand/seq/index.html does.

Particularly the SliceRandom and IteratorRandom utils are useful in many situations and their implementations don't seem to introduce any excessive complexity or unnecessary dependencies.

I wouldn't mind contributing code for this if there's a green light ๐Ÿ˜„

Doesn't compile on non-uwp Windows

So I currently get this when trying to compile nanorand on my Windows box:

  = note: libnanorand-3df99cf1d4f057c6.rlib(nanorand-3df99cf1d4f057c6.nanorand.3usfjb36-cgu.13.rcgu.o) : error LNK2019: unresolved external symbol BCryptGenRandom referenced in function _ZN8nanorand7entropy7windows19entropy_from_system17heb014656c23127ddE

This appears to be due to this being a non-uwp platform. Note that this happens even if the getrandom feature is enabled, because the windows entryopy module is still included with #[cfg(windows)], causing it to be linked in.

This appears to be the cfg that should be checked for to make sure BCryptGenRandom is available.

#[cfg(all(windows, target_vendor = "uwp"))]

OTOH, all the necessary platform detection already appears to be implemented in getrandom, so maybe we should just be relying on it instead? There's also some error handling being done which does not seem to be reproduced in nanorand as well.

Generating signed integers within a range yields biased/incorrect values

Generating an isize in the range [0, 192) produces values in the range [0, 96) heavily biased towards 0. Meanwhile, generating an i32 generates values outside the requested range.

Since unsigned integers are unaffected by this, a way around the issue for now is generating unsigned integers from 0 to max - min, casting the result to a signed integer, and then adding min back in.

// As an example, I'm using WyRand
fn generate_signed_range(rng: &mut WyRand, min: isize, max: isize) -> isize {
	rng.generate_range::<usize>(0, (max - min) as usize) as isize + min
}

I saw that this is being addressed in another branch, but I wanted to make this issue more visible for other users of this library.

ChaCha RNG

ChaCha20 is relatively simple to implement, in addition to being cryptographically secure and fast on just about any platform.

Would be a good way to scrape off that annoying disclaimer from the readme.

error[E0425]: cannot find function `backup_entropy` in this scope

Hi, one of my dependencies (wasmium-random) is using nanorand crate v0.6.1, my cargo throws an error while trying to build it, is there a way to change the v of nanorand inside of the other dep, also does the nanorand v0.7.0 solves this problem or are there any other solutions you could provide?

error: could not compile "nanorand" due to previous error

shuffle is biased

nanorand uses the naive shuffling algorithm that is biased: https://blog.codinghorror.com/the-danger-of-naivete/
Instead it should use proper Fisher-Yates.

Compare nanorand's implementation:

/// Shuffle a slice, using the RNG.

To rand's implementation: https://github.com/rust-random/rand/blob/e5ded134f57df2b3c2be0ca971440d5c0fd0ba43/src/seq/mod.rs#L586

Example to show the problem:

use nanorand::{WyRand, Rng};
// use rand::{thread_rng, seq::SliceRandom};

fn main() {
    let mut h = std::collections::HashMap::new();

    let mut rng = WyRand::new();
    // let mut rng = thread_rng();

    for _i in 0..600000 {
        let mut v = vec![1, 2, 3];

        rng.shuffle(&mut v);
        // v.shuffle(&mut rng);

        let count = h.entry(v).or_insert(0);
        *count += 1
    }

    println!("{:?}", h.values().collect::<Vec<_>>());
}

Expected output (rand):

[100075, 99739, 99808, 100459, 99995, 99924]

Actual output (nanorand):

[88976, 88802, 111499, 88925, 111185, 110613]

Fail to compile with `default-features = false` on macOS

 error[E0433]: failed to resolve: use of undeclared crate or module `std`
 --> /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/nanorand-0.5.2/src/entropy/darwin.rs:1:5
  |
1 | use std::ffi::c_void;
  |     ^^^ use of undeclared crate or module `std`

error[E0433]: failed to resolve: use of undeclared crate or module `std`
  --> /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/nanorand-0.5.2/src/entropy/darwin.rs:10:30
   |
10 |     unsafe { SecRandomCopyBytes(std::ptr::null(), out.len(), out.as_mut_ptr()) == 0 }
   |                                 ^^^ use of undeclared crate or module `std`

error[E0412]: cannot find type `c_void` in this scope
 --> /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/nanorand-0.5.2/src/entropy/darwin.rs:5:36
  |
5 |     fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> u32;
  |                                       ^^^^^^ not found in this scope
  |
help: consider importing this enum
  |
1 | use core::ffi::c_void;
  |

Would it be better to add macOS ci build target ?

Add float support for generate_range()

It would be cool to add float support to this function. So that we can generate random floats with custom ranges.

Example:

let rng = nanorand::tls_rng();
let rand_num: f64 = rng.generate_range(-3.0..5.5);

`nanorand::gen::RandomRange::random_range(lower, upper)` behaves incorrectly (`upper` is non-inclusive)

WyRand::new().generate_range::<u32>(0, 1); seems to always return 0, while the common understanding is that the upper parameter should be inclusive.

This seems to be the understanding of the crate's author as well, as in shuffle implementation:

fn shuffle<I, S: AsMut<[I]>>(&mut self, mut target: S) {
		let target = target.as_mut();
		let target_len = target.len();
		for idx in 0..target_len {
			let random_idx = self.generate_range::<usize>(0, target_len - 1);
			target.swap(idx, random_idx);
		}
	}

target_len - 1 basically means the last value in slice will never be selected as swap target, leading to some bizarre behavior, like shuffling a slice of len 2 always returns reversed slice, instead of giving roughly 50/50 distribution of original and reversed.

If the upper parameter was indeed meant to be non-inclusive, then I guess the docs should be updated and shuffle implementation fixed. If it was not, then the generate_range function has to be fixed.

Tested on crate v 0.5.2, Rust 1.48.0

EDIT: typos, formatting

feature rdseed fails to build on x86

Building feature rdseed or --all-features fails on 32-bit x86:

error[E0432]: unresolved import `core::arch::x86::_rdseed64_step`
  --> src/entropy.rs:58:6
   |
58 |     use core::arch::x86::_rdseed64_step as rdseed;
   |         ^^^^^^^^^^^^^^^^^--------------^^^^^^^^^^
   |         |                |
   |         |                help: a similar name exists in the module: `_rdseed16_step`
   |         no `_rdseed64_step` in `core_arch::arch::x86`

Undefined symbols for architecture x86_64: "_getrandom" on Mac OS X

๐Ÿ› Bug report

Description

Compilation failed on Mac OS X on travis CI.

Undefined symbols for architecture x86_64:
            "_getrandom", referenced from:
                nanorand::entropy::unix::entropy_from_system::h55951253ee7e2e83 in libnanorand-a83c92296e1a29be.rlib(nanorand-a83c92296e1a29be.nanorand.ow5fw3sw-cgu.6.rcgu.o)
          ld: symbol(s) not found for architecture x86_64

Link: https://travis-ci.org/github/Kogia-sima/sailfish/jobs/750030765

Also, getrandom syscall is only available if the kernel supports. We must check the availability on runtime.

https://github.com/rust-random/getrandom/blob/master/src/linux_android.rs#L27-L38

Environment

Software Version
rustc 1.48.0 (stable)
OS Mac OS X 10.13.6

Generated floats are not uniformly distributed

Hej ๐Ÿ‘‹

The documentation does not say anything about the distribution of the values, though one would assume that primitive values ought to be uniformly distributed. This is not the case for f32 and f64 based on

(u32::random(r) as f32) / (u32::MAX as f32)
and
(u64::random(r) as f64) / (u64::MAX as f64)

Floats above a certain value lose prevision, so using, say, u32::MAX and u32::MAX - 42 as the numerator both yield 1.0, which leads to a bias of the generated floats towards 1.0. This playground illustrates it a bit more: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=465eb2dcf33851195dde9f3a6601ea22

If you want to fix that, a solution would be to use the "safe" value provided by <float>::MANTISSA_DIGITS as the max instead of <int>::MAX. I could ope a PR for that if you like.

generate_range panics if the upper bound is 0

Example:

use nanorand::{tls_rng, RNG};

fn main() {
    println!("{}", tls_rng().generate_range::<usize>(3, 0));
}

Produces this output:

thread 'main' panicked at 'attempt to calculate the remainder with a divisor of zero', C:\Users\Andrew\.cargo\registry\src\github.com-1ecc6299db9ec823\nanorand-0.5.2\src\gen.rs:51:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

`WyRand::generate_range` is off by one.

fn example(){
    use nanorand::{Rng, WyRand};

    let mut rng = WyRand::new();
    println!("Random number: {}", rng.generate_range(1..=3));
    println!("Random number: {}", rng.generate_range(1..=3));
    println!("Random number: {}", rng.generate_range(1..=3));
    println!("Random number: {}", rng.generate_range(1..=3));
    println!("Random number: {}", rng.generate_range(1..=3));
    println!("Random number: {}", rng.generate_range(1..=3));

}

prints:

Random number: 1
Random number: 2
Random number: 1
Random number: 1
Random number: 0
Random number: 2

How does/does fill() work at all ?

I was trying to generate some uuids and it seems that fill() just takes mutable references to temporary copies of the bytes in an array. I have to map the initial zeroed array and just use generate() to get it to work.

        let mut rng = nanorand::tls_rng();
        let random_bytes = [0; 16];
        let random_bytes = random_bytes.map(|_| rng.generate::<u8>());

the above works

        rng.fill(random_bytes);
        rng.fill_bytes(random_bytes);

the above two do nothing, regardless of the declared mutability of random_bytes

Aliased mutable references with `tls_rand`

Due to the implementation of tls_rand & TlsWyRand, the following code is unsound, yet compiles:

use nanorand::tls_rng;

fn main() {
    // `x` and `y` both hold pointers to the same data in TLS (via `Rc`)
    let mut x = tls_rng();
    let mut y = tls_rng();

    // `TlsWyrand`'s `DerefMut` implementation turns this pointer into a mutable reference unconditionally
    let x_ref = &mut *x;
    let y_ref = &mut *y;

    // x_ref & y_ref now are both references to the same thing
    assert!(std::ptr::eq(x_ref, y_ref));
}

Support floats

It would be nice if it was possible to use nanorand to generate floats.

I know nothing about random number generation - would this be hard to implement?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.