use core::convert::Infallible;
use rand::rand_core::{Rng, SeedableRng, TryRng};
use rand_chacha::ChaCha20Rng;
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct ChaChaRng {
inner: ChaCha20Rng,
}
impl ChaChaRng {
pub fn from_seed(seed: [u8; 32]) -> Self {
Self {
inner: ChaCha20Rng::from_seed(seed),
}
}
#[cfg(feature = "std")]
pub fn from_os_rng() -> Self {
let mut seed = [0u8; 32];
for byte in &mut seed {
*byte = rand::random::<u8>();
}
Self::from_seed(seed)
}
#[inline]
pub fn next_u32(&mut self) -> u32 {
self.inner.next_u32()
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
self.inner.next_u64()
}
pub fn fill_bytes(&mut self, dest: &mut [u8]) {
self.inner.fill_bytes(dest);
}
}
impl TryRng for ChaChaRng {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok(self.next_u32())
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
Ok(self.next_u64())
}
fn try_fill_bytes(
&mut self,
dest: &mut [u8],
) -> Result<(), Self::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl SeedableRng for ChaChaRng {
type Seed = [u8; 32];
fn from_seed(seed: [u8; 32]) -> Self {
ChaChaRng::from_seed(seed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic_from_seed() {
let mut a = ChaChaRng::from_seed([42u8; 32]);
let mut b = ChaChaRng::from_seed([42u8; 32]);
for _ in 0..16 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn fill_bytes_unaligned() {
let mut rng = ChaChaRng::from_seed([7u8; 32]);
let mut buf = [0u8; 65];
rng.fill_bytes(&mut buf);
assert!(buf.iter().any(|&b| b != 0));
}
#[test]
fn matches_reference_rand_chacha() {
let seed = [9u8; 32];
let mut ours = ChaChaRng::from_seed(seed);
let mut reference = ChaCha20Rng::from_seed(seed);
for _ in 0..32 {
assert_eq!(ours.next_u64(), reference.next_u64());
}
}
#[test]
fn try_rng_and_seedable_impls() {
let mut rng = <ChaChaRng as SeedableRng>::from_seed([1u8; 32]);
assert!(TryRng::try_next_u32(&mut rng).is_ok());
assert!(TryRng::try_next_u64(&mut rng).is_ok());
let mut buf = [0u8; 16];
assert!(TryRng::try_fill_bytes(&mut rng, &mut buf).is_ok());
assert!(buf.iter().any(|&b| b != 0));
}
}