Skip to content

Commit

Permalink
Implement some of my suggestions for BlockRng
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Mar 7, 2018
1 parent c3c1eec commit a2c3059
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 43 deletions.
45 changes: 23 additions & 22 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
if self.index >= self.results.as_ref().len() {
let _ = self.core.generate(&mut self.results).unwrap();
self.core.generate(&mut self.results).unwrap();
self.index = 0;
}

Expand All @@ -224,34 +224,38 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
fn next_u64(&mut self) -> u64 {
let len = self.results.as_ref().len();

let index = self.index;
if index < len-1 {
self.index += 2;
// Read an u64 from the current index
let read_u64 = |results: &[u32], index| {
if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
unsafe { *(&self.results.as_ref()[index] as *const u32 as *const u64) }
// requires little-endian CPU supporting unaligned reads:
unsafe { *(&results[index] as *const u32 as *const u64) }
} else {
let x = self.results.as_ref()[index] as u64;
let y = self.results.as_ref()[index + 1] as u64;
let x = results[index] as u64;
let y = results[index + 1] as u64;
(y << 32) | x
}
};

let index = self.index;
if index < len-1 {
// Read an u64 from the current index
let index = self.index;
self.index += 2;
read_u64(self.results.as_ref(), index)
} else if index >= len {
let _ = self.core.generate(&mut self.results);
self.core.generate(&mut self.results).unwrap();
self.index = 2;
let x = self.results.as_ref()[0] as u64;
let y = self.results.as_ref()[1] as u64;
(y << 32) | x
read_u64(self.results.as_ref(), 0)
} else {
let x = self.results.as_ref()[len-1] as u64;
let _ = self.core.generate(&mut self.results);
self.core.generate(&mut self.results).unwrap();
self.index = 1;
let y = self.results.as_ref()[0] as u64;
(y << 32) | x
}
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
let _ = self.try_fill_bytes(dest);
self.try_fill_bytes(dest).unwrap();
}

// As an optimization we try to write directly into the output buffer.
Expand All @@ -260,7 +264,6 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
let mut filled = 0;
let mut res = Ok(());

// Continue filling from the current set of results
if self.index < self.results.as_ref().len() {
Expand All @@ -274,29 +277,27 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {

let len_remainder =
(dest.len() - filled) % (self.results.as_ref().len() * 4);
let len_direct = dest.len() - len_remainder;
let end_direct = dest.len() - len_remainder;

while filled < len_direct {
while filled < end_direct {
let dest_u32: &mut R::Results = unsafe {
::core::mem::transmute(dest[filled..].as_mut_ptr())
};
let res2 = self.core.generate(dest_u32);
if res2.is_err() && res.is_ok() { res = res2 };
self.core.generate(dest_u32)?;
filled += self.results.as_ref().len() * 4;
}
self.index = self.results.as_ref().len();

if len_remainder > 0 {
let res2 = self.core.generate(&mut self.results);
if res2.is_err() && res.is_ok() { res = res2 };
self.core.generate(&mut self.results)?;

let (consumed_u32, _) =
fill_via_u32_chunks(&mut self.results.as_ref(),
&mut dest[filled..]);

self.index = consumed_u32;
}
res
Ok(())
}

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
Expand Down
38 changes: 17 additions & 21 deletions src/reseeding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use impls::BlockRng;
/// methods will continue generating data from the wrapped PRNG without
/// reseeding.
#[derive(Debug)]
pub struct ReseedingRng<R, Rsdr>(BlockRng<Reseeder<R, Rsdr>>)
pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
where R: BlockRngCore<u32> + SeedableRng,
Rsdr: RngCore;

Expand All @@ -74,8 +74,8 @@ where R: BlockRngCore<u32> + SeedableRng,
let results_empty = R::Results::default();
ReseedingRng(
BlockRng {
core: Reseeder {
core: rng,
core: ReseedingCore {
inner: rng,
reseeder: reseeder,
threshold: threshold as i64,
bytes_until_reseed: threshold as i64,
Expand Down Expand Up @@ -113,14 +113,14 @@ impl<R: BlockRngCore<u32> + SeedableRng, Rsdr: RngCore> RngCore for ReseedingRng
}

#[derive(Debug)]
struct Reseeder<R, Rsdr> {
core: R,
struct ReseedingCore<R, Rsdr> {
inner: R,
reseeder: Rsdr,
threshold: i64,
bytes_until_reseed: i64,
}

impl<R, Rsdr> BlockRngCore<u32> for Reseeder<R, Rsdr>
impl<R, Rsdr> BlockRngCore<u32> for ReseedingCore<R, Rsdr>
where R: BlockRngCore<u32> + SeedableRng,
Rsdr: RngCore
{
Expand All @@ -138,29 +138,26 @@ where R: BlockRngCore<u32> + SeedableRng,
return self.reseed_and_generate(results);
}
self.bytes_until_reseed -= results.as_ref().len() as i64 * 4;
self.core.generate(results)
self.inner.generate(results)
}
}

impl<R, Rsdr> Reseeder<R, Rsdr>
impl<R, Rsdr> ReseedingCore<R, Rsdr>
where R: BlockRngCore<u32> + SeedableRng,
Rsdr: RngCore
{
/// Reseed the internal PRNG.
fn reseed(&mut self) -> Result<(), Error> {
R::from_rng(&mut self.reseeder).map(|result| self.core = result)
}

/// Reseed the internal PRNG.
///
/// If reseeding fails, this will try to work around errors intelligently
/// through some combination of retrying and delaying reseeding until later.
/// It will also report the error with `ErrorKind::Transient` with the
/// original error as cause.
fn auto_reseed(&mut self) -> Result<(), Error> {
/// by adjusting the delay until automatic reseeding next occurs.
/// It will still report the error but with kind changed to
/// `ErrorKind::Transient`.
fn reseed(&mut self) -> Result<(), Error> {
trace!("Reseeding RNG after {} generated bytes",
self.threshold - self.bytes_until_reseed);
if let Err(mut e) = self.reseed() {
if let Err(mut e) = R::from_rng(&mut self.reseeder)
.map(|result| self.inner = result)
{
let delay = match e.kind {
ErrorKind::Transient => 0,
kind @ _ if kind.should_retry() => self.threshold >> 8,
Expand All @@ -182,10 +179,9 @@ where R: BlockRngCore<u32> + SeedableRng,
results: &mut <Self as BlockRngCore<u32>>::Results)
-> Result<(), Error>
{
let res1 = self.auto_reseed();
let _result = self.reseed(); // reseeding errors are not fatal
self.bytes_until_reseed -= results.as_ref().len() as i64 * 4;
let res2 = self.core.generate(results);
if res2.is_err() { res2 } else { res1 }
self.inner.generate(results)
}
}

Expand Down

0 comments on commit a2c3059

Please sign in to comment.