use rand::{RngCore, SeedableRng};
use std::{
fmt,
io::{self, Write},
};
use structopt::StructOpt;
const BUFFER_SIZE: usize = 64 * 1024;
#[cfg(all(not(target_os = "emscripten"), target_pointer_width = "64"))]
type PcgRng = rand_pcg::Pcg64Mcg;
#[cfg(not(all(not(target_os = "emscripten"), target_pointer_width = "64")))]
type PcgRng = rand_pcg::Pcg32;
#[derive(Debug, StructOpt)]
#[structopt(
name = "rng",
about = "
A random data generator CLI tool.
Contains a number of (pseudo) random number generator algorithms. Given one of these it
prints an infinite stream of bytes generated from that algorithm to stdout.
",
rename_all = "kebab-case"
)]
struct Opt {
algorithm: Option<Algorithm>,
#[structopt(long)]
seed: Option<u64>,
}
#[derive(Debug)]
enum Algorithm {
Default,
Hc,
ChaCha8,
ChaCha12,
ChaCha20,
XorShift,
Pcg,
Os,
}
impl std::str::FromStr for Algorithm {
type Err = ParseAlgorithmError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"default" => Ok(Algorithm::Default),
"hc" => Ok(Algorithm::Hc),
"chacha" | "chacha20" => Ok(Algorithm::ChaCha20),
"chacha8" => Ok(Algorithm::ChaCha8),
"chacha12" => Ok(Algorithm::ChaCha12),
"xorshift" => Ok(Algorithm::XorShift),
"pcg" => Ok(Algorithm::Pcg),
"os" => Ok(Algorithm::Os),
_ => Err(ParseAlgorithmError(())),
}
}
}
macro_rules! init_rng {
($rng:ty, $seed:expr) => {
match $seed {
None => <$rng>::from_entropy(),
Some(seed) => <$rng>::seed_from_u64(seed),
}
};
}
fn main() {
let opt = Opt::from_args();
let seed = opt.seed;
match opt.algorithm.unwrap_or(Algorithm::Default) {
Algorithm::Default => generate(init_rng!(rand::rngs::StdRng, seed)),
Algorithm::Hc => generate(init_rng!(rand_hc::Hc128Rng, seed)),
Algorithm::ChaCha8 => generate(init_rng!(rand_chacha::ChaCha8Rng, seed)),
Algorithm::ChaCha12 => generate(init_rng!(rand_chacha::ChaCha12Rng, seed)),
Algorithm::ChaCha20 => generate(init_rng!(rand_chacha::ChaCha20Rng, seed)),
Algorithm::XorShift => generate(init_rng!(rand_xorshift::XorShiftRng, seed)),
Algorithm::Pcg => generate(init_rng!(PcgRng, seed)),
Algorithm::Os => {
if seed.is_some() {
eprintln!("WARNING: --seed is ignored when used with the OS PRNG");
}
generate(rand::rngs::OsRng);
}
}
}
fn generate(mut rng: impl RngCore) {
let stdout = io::stdout();
let mut stdout_lock = stdout.lock();
let mut buf = [0u8; BUFFER_SIZE];
loop {
rng.fill_bytes(&mut buf);
if stdout_lock.write_all(&buf).is_err() {
break;
}
}
}
#[derive(Debug)]
struct ParseAlgorithmError(());
impl fmt::Display for ParseAlgorithmError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Invalid algorithm. See --help for a list of valid options."
)
}
}
impl std::error::Error for ParseAlgorithmError {}