From cbd2cc74ca6835999d666c391c38645ec90c8044 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 19 Feb 2018 15:30:24 +0000 Subject: [PATCH 1/2] Add fill and try_fill methods to Rng --- src/lib.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index bd79a94a382..178df2938ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -265,7 +265,7 @@ #[cfg(all(feature="std", not(feature = "log")))] macro_rules! error { ($($x:tt)*) => () } -use core::{marker, mem}; +use core::{marker, mem, slice}; #[cfg(feature="std")] use std::cell::RefCell; #[cfg(feature="std")] use std::rc::Rc; #[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box; @@ -508,6 +508,71 @@ pub trait RngCore { /// /// [`RngCore`]: trait.RngCore.html pub trait Rng: RngCore + Sized { + /// Fill `dest` entirely with random bytes, where `dest` is any type + /// supporting [`AsByteSliceMut`], namely slices over primitive integer + /// types (`i8`, `i16`, `u32`, etc.). + /// + /// On big-endian platforms this performs byte-swapping to ensure + /// portability of results from reproducible generators. + /// + /// This uses [`fill_bytes`] internally which may handle some RNG errors + /// implicitly (e.g. waiting if the OS generator is not ready), but panics + /// on other errors. See also [`try_fill`] which returns errors. + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// + /// let mut arr = [0i8; 20]; + /// thread_rng().try_fill(&mut arr[..]); + /// ``` + /// + /// [`fill_bytes`]: trait.RngCore.html#method.fill_bytes + /// [`try_fill`]: trait.Rng.html#method.try_fill + /// [`AsByteSliceMut`]: trait.AsByteSliceMut.html + fn fill(&mut self, dest: &mut T) where Self: Sized { + self.fill_bytes(dest.as_byte_slice_mut()); + dest.to_le(); + } + + /// Fill `dest` entirely with random bytes, where `dest` is any type + /// supporting [`AsByteSliceMut`], namely slices over primitive integer + /// types (`i8`, `i16`, `u32`, etc.). + /// + /// On big-endian platforms this performs byte-swapping to ensure + /// portability of results from reproducible generators. + /// + /// This uses [`try_fill_bytes`] internally and forwards all RNG errors. In + /// some cases errors may be resolvable; see [`ErrorKind`] and + /// documentation for the RNG in use. If you do not plan to handle these + /// errors you may prefer to use [`fill`]. + /// + /// # Example + /// + /// ```rust + /// # use rand::Error; + /// use rand::{thread_rng, Rng}; + /// + /// # fn try_inner() -> Result<(), Error> { + /// let mut arr = [0u64; 4]; + /// thread_rng().try_fill(&mut arr[..])?; + /// # Ok(()) + /// # } + /// + /// # try_inner().unwrap() + /// ``` + /// + /// [`ErrorKind`]: enum.ErrorKind.html + /// [`try_fill_bytes`]: trait.RngCore.html#method.try_fill_bytes + /// [`fill`]: trait.Rng.html#method.fill + /// [`AsByteSliceMut`]: trait.AsByteSliceMut.html + fn try_fill(&mut self, dest: &mut T) -> Result<(), Error> where Self: Sized { + self.try_fill_bytes(dest.as_byte_slice_mut())?; + dest.to_le(); + Ok(()) + } + /// Sample a new value, using the given distribution. /// /// ### Example @@ -746,6 +811,62 @@ impl RngCore for Box { } } +/// Trait for casting types to byte slices +/// +/// This is used by the [`fill`] and [`try_fill`] methods. +/// +/// [`fill`]: trait.Rng.html#method.fill +/// [`try_fill`]: trait.Rng.html#method.try_fill +pub trait AsByteSliceMut { + /// Return a mutable reference to self as a byte slice + fn as_byte_slice_mut<'a>(&'a mut self) -> &'a mut [u8]; + + /// Call `to_le` on each element (i.e. byte-swap on Big Endian platforms). + fn to_le(&mut self); +} + +impl AsByteSliceMut for [u8] { + fn as_byte_slice_mut<'a>(&'a mut self) -> &'a mut [u8] { + self + } + + fn to_le(&mut self) {} +} + +macro_rules! impl_as_byte_slice { + ($t:ty) => { + impl AsByteSliceMut for [$t] { + fn as_byte_slice_mut<'a>(&'a mut self) -> &'a mut [u8] { + unsafe { + slice::from_raw_parts_mut(&mut self[0] + as *mut $t + as *mut u8, + self.len() * mem::size_of::<$t>() + ) + } + } + + fn to_le(&mut self) { + for mut x in self { + *x = x.to_le(); + } + } + } + } +} + +impl_as_byte_slice!(u16); +impl_as_byte_slice!(u32); +impl_as_byte_slice!(u64); +#[cfg(feature="i128_support")] impl_as_byte_slice!(u128); +impl_as_byte_slice!(usize); +impl_as_byte_slice!(i8); +impl_as_byte_slice!(i16); +impl_as_byte_slice!(i32); +impl_as_byte_slice!(i64); +#[cfg(feature="i128_support")] impl_as_byte_slice!(i128); +impl_as_byte_slice!(isize); + /// Iterator which will generate a stream of random items. /// /// This iterator is created via the [`gen_iter`] method on [`Rng`]. @@ -1317,6 +1438,24 @@ mod test { } } } + + #[test] + fn test_fill() { + let x = 9041086907909331047; // a random u64 + let mut rng = ConstRng { i: x }; + + // Convert to byte sequence and back to u64; byte-swap twice if BE. + let mut array = [0u64; 2]; + rng.fill(&mut array[..]); + assert_eq!(array, [x, x]); + assert_eq!(rng.next_u64(), x); + + // Convert to bytes then u32 in LE order + let mut array = [0u32; 2]; + rng.fill(&mut array[..]); + assert_eq!(array, [x as u32, (x >> 32) as u32]); + assert_eq!(rng.next_u32(), x as u32); + } #[test] fn test_gen_range() { From 5da66c35fb13035a3b8edda8632a613a6b3c2259 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Mon, 19 Feb 2018 16:42:18 +0000 Subject: [PATCH 2/2] AsMutByteSlice: support arrays up to length 32 Usage of recursive macros appears to have some compile time hit, but with a single macro and generic impl it's not too much (directly recursing for each type is far worse). --- src/lib.rs | 30 ++++++++++++++++++++++++------ src/seq.rs | 4 ++-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 178df2938ee..d7080ae4a63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -508,9 +508,9 @@ pub trait RngCore { /// /// [`RngCore`]: trait.RngCore.html pub trait Rng: RngCore + Sized { - /// Fill `dest` entirely with random bytes, where `dest` is any type - /// supporting [`AsByteSliceMut`], namely slices over primitive integer - /// types (`i8`, `i16`, `u32`, etc.). + /// Fill `dest` entirely with random bytes (uniform value distribution), + /// where `dest` is any type supporting [`AsByteSliceMut`], namely slices + /// and arrays over primitive integer types (`i8`, `i16`, `u32`, etc.). /// /// On big-endian platforms this performs byte-swapping to ensure /// portability of results from reproducible generators. @@ -536,9 +536,9 @@ pub trait Rng: RngCore + Sized { dest.to_le(); } - /// Fill `dest` entirely with random bytes, where `dest` is any type - /// supporting [`AsByteSliceMut`], namely slices over primitive integer - /// types (`i8`, `i16`, `u32`, etc.). + /// Fill `dest` entirely with random bytes (uniform value distribution), + /// where `dest` is any type supporting [`AsByteSliceMut`], namely slices + /// and arrays over primitive integer types (`i8`, `i16`, `u32`, etc.). /// /// On big-endian platforms this performs byte-swapping to ensure /// portability of results from reproducible generators. @@ -867,6 +867,24 @@ impl_as_byte_slice!(i64); #[cfg(feature="i128_support")] impl_as_byte_slice!(i128); impl_as_byte_slice!(isize); +macro_rules! impl_as_byte_slice_arrays { + ($n:expr,) => {}; + ($n:expr, $N:ident, $($NN:ident,)*) => { + impl_as_byte_slice_arrays!($n - 1, $($NN,)*); + + impl AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut { + fn as_byte_slice_mut<'a>(&'a mut self) -> &'a mut [u8] { + self[..].as_byte_slice_mut() + } + + fn to_le(&mut self) { + self[..].to_le() + } + } + }; +} +impl_as_byte_slice_arrays!(32, N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,); + /// Iterator which will generate a stream of random items. /// /// This iterator is created via the [`gen_iter`] method on [`Rng`]. diff --git a/src/seq.rs b/src/seq.rs index 9bb328dc209..8562f55a19b 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -227,7 +227,7 @@ fn sample_indices_cache( #[cfg(test)] mod test { use super::*; - use {XorShiftRng, RngCore, SeedableRng}; + use {XorShiftRng, Rng, SeedableRng}; #[cfg(not(feature="std"))] use alloc::Vec; @@ -304,7 +304,7 @@ mod test { for length in 1usize..max_range { let amount = r.gen_range(0, length); let mut seed = [0u8; 16]; - r.fill_bytes(&mut seed); + r.fill(&mut seed); // assert that the two index methods give exactly the same result let inplace = sample_indices_inplace(