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
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! A small random number generator hacked on top of Rust's standard library.
//!
//! ### Origins
//! Rust, as you may well know, has support for `HashMap`s.
//! These maps, as per their name, require hashing.
//! Hashing, of course, takes some data, and produces a random representation of that data.
//! Where we hash, random numbers follow!
//!
//! > "Well, it seemed like a good idea at the time..."
//! >
//! > — You explaining `attorand` to your smart contract shareholders, probably.
//!
//! ### Why `attorand`?
//! Other crates, like `rand`, can be hard to use cross-platform,
//! Because they rely on the system's source of randomness.
//! `attorand`, however, does not have this limitation,
//! and provides a firehose of random bytes to drink from.
//!
//! ### How does it work?
//! Internally, `attorand` works by repeatedly hashing a seed,
//! using the output of the last round as the input to the next.
//! The algorithm used is SipHash 1-3, as per Rust's
//! [standard library docs](https://doc.rust-lang.org/std/collections/struct.HashMap.html),
//! but note that this is "subject to change at any point in the future".
//!
//! ### Addendum
//! Please don't use this for anything that needs cryptographic randomness.
//! (You knew that.)
//!
//! I hold no further responsibility for this abomination.
//!
//! Welp, That's it from me. What are you waiting for?
//! Allocate a fresh [`Rng`] to get started!

use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;

/// The default seed for the [`Rng`] if no other seed is specified.
/// There is nothing special about this number.
/// Please don't try to figure out why this number is special.
pub const RNG_SEED: u64 = 0x_6865_636B_7965_6168;

/// A portable random number generator built on Rust's default `HashMap` hasher.
/// Has no dependencies other than the standard library (doh).
/// This random number generator is not cryptographically secure, not the fastest, yada yada yada,
/// so don't use it unless you have a really good reason to.
pub struct Rng {
    data:   u64,
    hasher: DefaultHasher,
}

impl Rng {
    /// Creates a new pseudorandom number, using the default seed.
    /// Will produce the same series of numbers every time.
    /// Use [`Rng::new_with_seed`] to spice things up a little!
    pub fn new_default() -> Rng { Rng::new_with_seed(RNG_SEED) }

    /// Create a new random number generator for a given seed.
    /// Don't overthink it, you can just use the current iteration of a loop as a seed
    /// or something to get a new random number generator on each iteration.
    pub fn new_with_seed(seed: u64) -> Rng {
        Rng {
            data:   seed,
            hasher: DefaultHasher::new(),
        }
    }

    /// Return the next unsigned 64-bit natural number
    /// less than or equal to a given number.
    /// uses rejection sampling, so timing attacks are a thing.
    ///
    /// But you're not using this for crypto, right?
    ///
    /// But you're not using this for crypto... right!?
    pub fn next_u64_max(&mut self, max: u64) -> u64 {
        let mask = 2_u64.pow(64 - (max as u64).leading_zeros()) - 1;
        let mut out = self.next_u64() & mask;
        while out > max { out = self.next_u64() & mask }
        out
    }

    /// Return the next 64-bit unsigned integer.
    /// If you want to constrict the range a bit (say, before casting to a `u32`),
    /// try using [`Rng::next_u64_max`].
    pub fn next_u64(&mut self) -> u64 {
        let mut out = 0;
        for i in self.take(8) {
            out = out << 8 | i as u64;
        }
        out
    }

    /// Return a random byte.
    /// If you need more than a byte, use [`Rng::next_u64`].
    /// If you're looking for an infinite byte generator,
    /// try using this bad boy as an iterator.
    pub fn next_byte(&mut self) -> u8 {
        self.next().unwrap()
    }

    /// Return a random boolean.
    /// Super handy for choosing between a couple of possibilities.
    /// Oh, you need more than a `bool`?
    /// why not try [`Rng::next_byte`]?
    pub fn next_bool(&mut self) -> bool {
        self.next().unwrap() % 2 == 0
    }
}

impl Iterator for Rng {
    type Item = u8;

    /// Generates an infinite stream of random bytes.
    /// Used as a source for all other functions.
    ///
    /// Note how hashing gives us 8 fresh bytes,
    /// but we waste a lot of time XOR-in' them all together
    /// to produce a single byte at a time.
    /// Easy 8x speedup for version 2 of the library.
    fn next(&mut self) -> Option<u8> {
        self.hasher.write_u64(self.data);
        self.data = self.hasher.finish();
        Some(self.data.to_be_bytes().iter().fold(0, |a, b| a^b))
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    /// Just a quick sanity check for the 'less than or equal to' statement.
    /// These are all the tests a cryptographically-secure random number generator needs.
    #[test]
    fn it_works() {
        let mut rng = Rng::new_default();
        for _ in 0..100 {
            let randers = rng.next_u64_max(2);
            println!("{}", randers);
            assert!(randers <= 2);
        }
    }
}