Skip to content

Commit

Permalink
Remove Result from BlockRngCore::generate
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Mar 14, 2018
1 parent 963be0c commit 08924a3
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 73 deletions.
37 changes: 12 additions & 25 deletions rand-core/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,6 @@ pub fn next_u64_via_fill<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
/// implementation to write the results directly to the destination slice.
/// No generated values are ever thown away.
///
/// Although `BlockCoreRng::generate` can return a `Result`, we assume all PRNGs
/// to be infallible, and for the `Result` to only have a signaling function.
/// Therefore, the error is only reported by `try_fill_bytes`, all other
/// functions squelch the error.
///
/// For easy initialization `BlockRng` also implements [`SeedableRng`].
///
/// [`BlockRngCore`]: ../BlockRngCore.t.html
Expand Down Expand Up @@ -209,7 +204,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);
self.core.generate(&mut self.results);
self.index = 0;
}

Expand Down Expand Up @@ -239,29 +234,24 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
// Read an u64 from the current index
read_u64(self.results.as_ref(), index)
} else if index >= len {
let _ = self.core.generate(&mut self.results);
self.core.generate(&mut self.results);
self.index = 2;
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);
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);
}

// As an optimization we try to write directly into the output buffer.
// This is only enabled for little-endian platforms where unaligned writes
// are known to be safe and fast.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
fn fill_bytes(&mut self, dest: &mut [u8]) {
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 @@ -281,33 +271,27 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
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
}

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
let mut res = Ok(());
fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut read_len = 0;
while read_len < dest.len() {
if self.index >= self.results.as_ref().len() {
let res2 = self.core.generate(&mut self.results);
if res2.is_err() && res.is_ok() { res = res2 };
self.core.generate(&mut self.results);
self.index = 0;
}
let (consumed_u32, filled_u8) =
Expand All @@ -317,7 +301,10 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
self.index += consumed_u32;
read_len += filled_u8;
}
res
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
}
}

Expand Down
11 changes: 1 addition & 10 deletions rand-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,7 @@ pub trait BlockRngCore<T>: Sized {
type Results: AsRef<[T]> + Default;

/// Generate a new block of results.
///
/// The result type is unnecessary for PRNGs, which we assume to be
/// infallible. It only has a signalling function, for example to report a
/// failed reseed, that the PRNG is used beyond its limits, or because it
/// 'noticed' some kind of interference (like fork protection).
///
/// In all cases user code is allowed to ignore any errors from `generate`
/// and to expect the generator to keep working, even though it may not be
/// in the most ideal conditions.
fn generate(&mut self, results: &mut Self::Results) -> Result<(), Error>;
fn generate(&mut self, results: &mut Self::Results);
}

/// A marker trait for an `Rng` which may be considered for use in
Expand Down
9 changes: 4 additions & 5 deletions src/prng/chacha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ macro_rules! double_round{
impl BlockRngCore<u32> for ChaChaCore {
type Results = [u32; STATE_WORDS];

fn generate(&mut self, results: &mut Self::Results) -> Result<(), Error> {
fn generate(&mut self, results: &mut Self::Results) {
// For some reason extracting this part into a separate function
// improves performance by 50%.
fn core(results: &mut [u32; STATE_WORDS],
Expand All @@ -242,13 +242,12 @@ impl BlockRngCore<u32> for ChaChaCore {

// update 128-bit counter
self.state[12] = self.state[12].wrapping_add(1);
if self.state[12] != 0 { return Ok(()) };
if self.state[12] != 0 { return; };
self.state[13] = self.state[13].wrapping_add(1);
if self.state[13] != 0 { return Ok(()) };
if self.state[13] != 0 { return; };
self.state[14] = self.state[14].wrapping_add(1);
if self.state[14] != 0 { return Ok(()) };
if self.state[14] != 0 { return; };
self.state[15] = self.state[15].wrapping_add(1);
Ok(())
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/prng/hc128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ impl fmt::Debug for Hc128Core {
impl BlockRngCore<u32> for Hc128Core {
type Results = [u32; 16];

fn generate(&mut self, results: &mut Self::Results) -> Result<(), Error> {
fn generate(&mut self, results: &mut Self::Results) {
assert!(self.counter1024 % 16 == 0);

let cc = self.counter1024 % 512;
Expand Down Expand Up @@ -159,7 +159,6 @@ impl BlockRngCore<u32> for Hc128Core {
results[15] = self.step_q(cc+15, dd+0, cc+12, cc+5, cc+3);
}
self.counter1024 = self.counter1024.wrapping_add(16);
Ok(())
}
}

Expand Down
45 changes: 14 additions & 31 deletions src/reseeding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,11 @@ use rand_core::impls::BlockRng;
///
/// # Error handling
///
/// It is usually best to use the infallible methods `next_u32`, `next_u64` and
/// `fill_bytes`, because `ReseedingRng` and the wrapped PRNG always continue
/// working, they never need error handling. Only use `try_fill_bytes` if you
/// care to (and are prepared to) handle errors due to a failed reseed.
///
/// If reseeding fails, `try_fill_bytes` is the only `Rng` method to report it.
/// For all other `Rng` methods, `ReseedingRng` will not panic but try to
/// handle the error intelligently through some combination of retrying and
/// delaying reseeding until later. If handling the source error fails these
/// methods will continue generating data from the wrapped PRNG without
/// reseeding.
/// Although extremely unlikely, reseeding the wrapped PRNG can fail.
/// `ReseedingRng` will never panic but try to handle the error intelligently
/// through some combination of retrying and delaying reseeding until later.
/// If handling the source error fails `ReseedingRng` will continue generating
/// data from the wrapped PRNG without reseeding.
#[derive(Debug)]
pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
where R: BlockRngCore<u32> + SeedableRng,
Expand Down Expand Up @@ -135,19 +129,15 @@ where R: BlockRngCore<u32> + SeedableRng,
{
type Results = <R as BlockRngCore<u32>>::Results;

fn generate(&mut self, results: &mut Self::Results) -> Result<(), Error> {
fn generate(&mut self, results: &mut Self::Results) {
if self.bytes_until_reseed <= 0 {
// We want to reseed here, and generate results later in the
// function. If generating results fail, we should return the error
// from that. If generating results succeeded, but reseeding failed,
// we should return the error from reseeding.
// The only way to get this behaviour without destroying performance
// was to split part of the function out into a
// `reseed_and_generate` method.
// We get better performance by not calling only `auto_reseed` here
// and continuing with the rest of the function, but by directly
// returning from a non-inlined function.
return self.reseed_and_generate(results);
}
self.bytes_until_reseed -= results.as_ref().len() as i64 * 4;
self.inner.generate(results)
self.inner.generate(results);
}
}

Expand All @@ -167,12 +157,10 @@ where R: BlockRngCore<u32> + SeedableRng,
///
/// If reseeding fails, this will try to work around errors intelligently
/// by adjusting the delay until automatic reseeding next occurs.
/// It will still report the error but with kind changed to
/// `ErrorKind::Transient`.
fn auto_reseed(&mut self) -> Result<(), Error> {
fn auto_reseed(&mut self) {
trace!("Reseeding RNG after {} generated bytes",
self.threshold - self.bytes_until_reseed);
if let Err(mut e) = self.reseed() {
if let Err(e) = self.reseed() {
let delay = match e.kind {
ErrorKind::Transient => 0,
kind @ _ if kind.should_retry() => self.threshold >> 8,
Expand All @@ -181,23 +169,18 @@ where R: BlockRngCore<u32> + SeedableRng,
warn!("Reseeding RNG delayed reseeding by {} bytes due to \
error from source: {}", delay, e);
self.bytes_until_reseed = delay;
e.kind = ErrorKind::Transient;
Err(e)
} else {
self.bytes_until_reseed = self.threshold;
Ok(())
}
}

#[inline(never)]
fn reseed_and_generate(&mut self,
results: &mut <Self as BlockRngCore<u32>>::Results)
-> Result<(), Error>
{
let res1 = self.auto_reseed();
self.auto_reseed();
self.bytes_until_reseed -= results.as_ref().len() as i64 * 4;
let res2 = self.inner.generate(results);
if res2.is_err() { res2 } else { res1 }
self.inner.generate(results);
}
}

Expand Down

0 comments on commit 08924a3

Please sign in to comment.