nanorand

repository·main·Indexed 42 days ago

https://github.com/absolucy/nanorand-rs

A tiny, fast, zero-dependency Rust library for random number generation (version 0.8.0). It provides high-performance non-cryptographic RNGs like WyRand and Pcg64, as well as cryptographically secure options via ChaCha. Features include thread-local RNGs, range-based generation, and a BufferedRng wrapper to reduce entropy waste.

Tokens
5.6K
Snippets
23
Records
30
Agent score
53%

What's inside nanorand

  1. Compare nanorand with other RNG libraries

    main

    nanorand is designed for speed and minimal dependencies. Here is how it compares to other common choices:

    • vs rand: rand is a comprehensive, feature-rich library that includes unsafe code in core implementations. nanorand is a focused, lightweight alternative.
    • vs fastrand, oorandom, random-fast-rng, or randomize: These libraries implement the PCG family of RNGs. nanorand's WyRand implementation is significantly faster while providing 64 random bits and offering CSPRNG options.
    • vs getrandom: getrandom is an entropy source provider, not a random number generator. nanorand can use getrandom as an entropy source via a feature flag.
  2. Configure entropy sources via feature flags

    main

    nanorand uses a prioritized list of entropy sources. You can influence this behavior using feature flags:

    1. getrandom: If enabled, getrandom::getrandom is used as the primary entropy source.
    2. rdseed: On x86/x86-64, if OS entropy is unavailable, it attempts to use the rdseed intrinsic.
    3. OS Defaults: If neither of the above are used, it falls back to OS-specific sources:
      • Linux/Android: getrandom syscall.
      • macOS/iOS: SecRandomCopyBytes (Security.framework).
      • OpenBSD/FreeBSD: arc4random_buf.
      • Windows: RtlGenRandom (or BCryptGenRandom for UWP).
  3. Buffer random bytes with BufferedRng

    main

    The BufferedRng (requires the alloc feature) provides a wrapper for RNGs to prevent wasting bits. When an RNG generates more bits than requested (e.g., WyRand generates 64 bits but you only need 5 bytes), BufferedRng stores the remaining bits in an internal buffer for subsequent calls. Use .buffered() to check how many bytes remain in the buffer.

    use nanorand::{Rng, BufferedRng, WyRand};
    
    let mut thingy = [0u8; 5];
    let mut rng = BufferedRng::new(WyRand::new());
    rng.fill(&mut thingy);
    // As WyRand generates 8 bytes of output, and our target is only 5 bytes,
    // 3 bytes will remain in the buffer.
    assert_eq!(rng.buffered(), 3);
  4. Use `BufferedRng` to reduce entropy waste

    main

    The BufferedRng is a wrapper for any Rng implementation. It improves efficiency by keeping unused bytes from the last call to Rng::rand in an internal buffer and using them for subsequent requests. This is particularly useful when the underlying generator produces more bytes (e.g., 8 bytes) than the requested amount (e.g., 5 bytes).

    use nanorand::{Rng, BufferedRng, WyRand};
    
    let mut thingy = [0u8; 5];
    let mut rng = BufferedRng::new(WyRand::new());
    rng.fill(&mut thingy);
    // As WyRand generates 8 bytes of output, and our target is only 5 bytes, 
    // 3 bytes will remain in the buffer.
    assert_eq!(rng.buffered(), 3);
  5. How `RandomGen` and `RandomRange` work together

    main

    The library uses a trait-based approach to provide type-safe random generation.

    1. RandomGen<Generator, OUTPUT>: The base trait. It defines random(rng: &mut Generator) -> Self. It is used for generating a single value of a type without regard to bounds.
    2. RandomRange<Generator, OUTPUT>: A sub-trait that requires RandomGen. It defines random_range<Bounds>(rng: &mut Generator, bounds: Bounds) -> Self. It uses the underlying random implementation to produce values that fit within the specified RangeBounds.

    This hierarchy ensures that any type capable of being generated randomly can also be constrained to a range.

  6. Generate random numbers with nanorand

    main

    You can generate random numbers using an initialized RNG instance or a thread-local RNG.

    To use an initialized RNG, instantiate a specific algorithm like WyRand and call .generate::<T>() where T is the desired type.

    To use a thread-local RNG (requires the tls feature), call nanorand::tls_rng() to get a handle to a thread-local WyRand instance.

    use nanorand::{Rng, WyRand};
    
    // Using an initialized RNG
    let mut rng = WyRand::new();
    println!("Random number: {}", rng.generate::<u64>());
    
    // Using a thread-local RNG
    let mut rng = nanorand::tls_rng();
    println!("Random number: {}", rng.generate::<u64>());
  7. Buffer random bytes for efficiency

    main

    The BufferedRng wrapper can be used to improve performance when you need to fill small buffers frequently. It wraps an existing RNG and maintains an internal buffer. When you call fill(), it uses the underlying RNG to populate the buffer, potentially generating more bytes than requested to minimize calls to the underlying generator.

    You can check how many bytes remain in the buffer using the buffered() method.

    Example:

    use nanorand::{Rng, BufferedRng, WyRand};
    
    let mut thingy = [0u8; 5];
    let mut rng = BufferedRng::new(WyRand::new());
    rng.fill(&mut thingy);
    // As WyRand generates 8 bytes of output, and our target is only 5 bytes,
    // 3 bytes will remain in the buffer.
    assert_eq!(rng.buffered(), 3);
  8. Generate random numbers with an initialized RNG

    main

    To use a specific RNG implementation, instantiate it using its new() method and call generate::<T>() to produce a random value of type T. This approach gives you full control over the RNG instance.

    Example using WyRand:

    use nanorand::{Rng, WyRand};
    
    let mut rng = WyRand::new();
    println!("Random number: {}", rng.generate::<u64>());
  9. Generate random numbers with a thread-local RNG

    main

    If you want to avoid the overhead of managing your own RNG instance, you can use a thread-local RNG. This is useful for high-performance scenarios where multiple threads need access to an RNG without contention. This requires the tls feature to be enabled.

    Example using nanorand::tls_rng():

    use nanorand::Rng;
    
    let mut rng = nanorand::tls_rng();
    println!("Random number: {}", rng.generate::<u64>());
  10. Shuffle a Vec

    main

    You can shuffle the elements of a Vec in-place using the shuffle method provided by the Rng trait.

    Example:

    use nanorand::{Rng, WyRand};
    
    let mut rng = WyRand::new();
    let mut items = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    rng.shuffle(&mut items);
  11. Generate random numbers in a range

    main

    You can generate a random number within a specific range using the generate_range method. This method accepts any type that implements the appropriate range traits (e.g., std::ops::Range or std::ops::RangeInclusive).

    Example:

    use nanorand::{Rng, WyRand};
    
    let mut rng = WyRand::new();
    println!("Random number between 1 and 100: {}", rng.generate_range(1_u64..=100));
    println!("Random number between -100 and 50: {}", rng.generate_range(-100_i64..=50));
  12. Configure nanorand via feature flags

    main

    Use the following feature flags to customize the nanorand build:

    • alloc (default): Enables alloc lib features (e.g., BufferedRng).
    • std (default): Enables std lib features (e.g., seeding from OS entropy). Requires alloc.
    • tls (default): Enables thread-local WyRand RNG. Requires std.
    • wyrand (default): Enables WyRand RNG.
    • pcg64 (default): Enables Pcg64 RNG.
    • chacha: Enables ChaCha RNG. Requires Rust 1.47+.
    • rdseed: Uses rdseed intrinsic on x86/x86-64 if OS entropy is unavailable.
    • zeroize: Implements the Zeroize trait for all RNGs.
    • getrandom: Uses the getrandom crate as an entropy source.