Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 1 addition & 83 deletions src/le.rs
Original file line number Diff line number Diff line change
@@ -1,76 +1,8 @@
//! # Little-Endian utilities
//!
//! For cross-platform reproducibility, Little-Endian order (least-significant
//! part first) has been chosen as the standard for inter-type conversion.
//! For example, ``next_u64_via_u32`] takes `u32`
//! values `x, y`, then outputs `(y << 32) | x`.
//!
//! Byte-swapping (like the std `to_le` functions) is only needed to convert
//! to/from byte sequences, and since its purpose is reproducibility,
//! non-reproducible sources (e.g. `OsRng`) need not bother with it.
//!
//! ### Implementing [`RngCore`]
//!
//! Usually an implementation of [`RngCore`] will implement one of the three
//! methods over its internal source. The following helpers are provided for
//! the remaining implementations.
//!
//! **`fn next_u32`:**
//! - `self.next_u64() as u32`
//! - `(self.next_u64() >> 32) as u32`
//! - <code>[next_u32_via_fill][](self)</code>
//!
//! **`fn next_u64`:**
//! - <code>[next_u64_via_u32][](self)</code>
//! - <code>[next_u64_via_fill][](self)</code>
//!
//! **`fn fill_bytes`:**
//! - <code>[fill_bytes_via_next][](self, dest)</code>
//!
//! ### Implementing [`SeedableRng`]
//!
//! In many cases, [`SeedableRng::Seed`] must be converted to `[u32]` or
//! `[u64]`. The following helpers are provided:
//!
//! - [`read_u32_into`]
//! - [`read_u64_into`]

use crate::RngCore;

#[allow(unused)]
use crate::SeedableRng;

/// Implement `next_u64` via `next_u32`, little-endian order.
pub fn next_u64_via_u32<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
// Use LE; we explicitly generate one value before the next.
let x = u64::from(rng.next_u32());
let y = u64::from(rng.next_u32());
(y << 32) | x
}

/// Implement `fill_bytes` via `next_u64` and `next_u32`, little-endian order.
///
/// The fastest way to fill a slice is usually to work as long as possible with
/// integers. That is why this method mostly uses `next_u64`, and only when
/// there are 4 or less bytes remaining at the end of the slice it uses
/// `next_u32` once.
pub fn fill_bytes_via_next<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) {
let mut left = dest;
while left.len() >= 8 {
let (l, r) = { left }.split_at_mut(8);
left = r;
let chunk: [u8; 8] = rng.next_u64().to_le_bytes();
l.copy_from_slice(&chunk);
}
let n = left.len();
if n > 4 {
let chunk: [u8; 8] = rng.next_u64().to_le_bytes();
left.copy_from_slice(&chunk[..n]);
} else if n > 0 {
let chunk: [u8; 4] = rng.next_u32().to_le_bytes();
left.copy_from_slice(&chunk[..n]);
}
}

pub(crate) trait Observable: Copy {
type Bytes: Sized + AsRef<[u8]>;
fn to_le_bytes(self) -> Self::Bytes;
Expand Down Expand Up @@ -120,20 +52,6 @@ pub(crate) fn fill_via_chunks<T: Observable>(src: &[T], dest: &mut [u8]) -> (usi
(num_chunks, byte_len)
}

/// Implement `next_u32` via `fill_bytes`, little-endian order.
pub fn next_u32_via_fill<R: RngCore + ?Sized>(rng: &mut R) -> u32 {
let mut buf = [0; 4];
rng.fill_bytes(&mut buf);
u32::from_le_bytes(buf)
}

/// Implement `next_u64` via `fill_bytes`, little-endian order.
pub fn next_u64_via_fill<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
let mut buf = [0; 8];
rng.fill_bytes(&mut buf);
u64::from_le_bytes(buf)
}
Comment on lines -123 to -135
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have no replacement, but are rarely (if ever: getrandom doesn't use them) needed, so removal is fine.


/// Fills `dst: &mut [u32]` from `src`
///
/// Reads use Little-Endian byte order, allowing portable reproduction of `dst`
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use core::{fmt, ops::DerefMut};

pub mod block;
pub mod le;
mod sealed;
pub mod utils;

/// Implementation-level interface for RNGs
///
Expand Down
42 changes: 42 additions & 0 deletions src/sealed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/// Sealed trait implemented for `u32` and `u64`.
pub trait Sealed: Default + Copy + TryFrom<usize> {
type Bytes: Sized + AsRef<[u8]> + for<'a> TryFrom<&'a [u8]>;

fn from_usize(val: usize) -> Self;
fn to_le_bytes(self) -> Self::Bytes;
fn from_le_bytes(bytes: Self::Bytes) -> Self;
}

impl Sealed for u32 {
type Bytes = [u8; 4];

#[inline(always)]
fn from_usize(val: usize) -> Self {
val.try_into().unwrap()
}
#[inline(always)]
fn to_le_bytes(self) -> Self::Bytes {
u32::to_le_bytes(self)
}
#[inline(always)]
fn from_le_bytes(bytes: Self::Bytes) -> Self {
u32::from_le_bytes(bytes)
}
}

impl Sealed for u64 {
type Bytes = [u8; 8];

#[inline(always)]
fn from_usize(val: usize) -> Self {
val.try_into().unwrap()
}
#[inline(always)]
fn to_le_bytes(self) -> Self::Bytes {
u64::to_le_bytes(self)
}
#[inline(always)]
fn from_le_bytes(bytes: Self::Bytes) -> Self {
u64::from_le_bytes(bytes)
}
}
Loading