feat: add byte-fill convenience construction
Add OsRandom::fill_bytes so callers can fill a byte slice without importing std::io::Read. This keeps the common raw-byte use case close to the rest of the crate's typed convenience API while still forwarding to the same /dev/urandom handle. Implement Default for OsRandom as the infallible convenience constructor. The fallible OsRandom::try_new API remains the right choice when the caller wants to handle an unavailable operating-system randomness source explicitly. Document the public constructors and byte-fill helper with working examples, and add a runnable bytes example for the top-level workflow. Test Plan: - cargo test - cargo clippy - cargo clippy --benches - cargo clippy --tests - cargo +nightly fmt Refs: IDEAS.md ergonomics backlog
This commit is contained in:
+63
@@ -4,6 +4,16 @@
|
||||
//! `/dev/urandom` with helpers for reading primitive integers, sampling
|
||||
//! uniformly without modulo bias, and generating random ASCII strings from a
|
||||
//! caller-supplied alphabet. Common alphabets live in [`charset`].
|
||||
//!
|
||||
//! # Project layout
|
||||
//!
|
||||
//! - [`OsRandom`] owns an open `/dev/urandom` handle.
|
||||
//! - Constructors open or duplicate that handle.
|
||||
//! - Primitive readers turn raw bytes into integer values.
|
||||
//! - Convenience byte readers fill caller-provided buffers.
|
||||
//! - Sampling helpers build unbiased higher-level choices from those primitive
|
||||
//! readers.
|
||||
//! - [`charset`] contains reusable ASCII alphabets for string generation.
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
@@ -102,17 +112,70 @@ macro_rules! os_random_get_integer_impls {
|
||||
impl OsRandom {
|
||||
/// # Errors
|
||||
/// Returns any I/O error produced while opening `/dev/urandom`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut rng = ez_urandom::OsRandom::try_new()?;
|
||||
/// let value = rng.get_u64()?;
|
||||
/// # let _ = value;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn try_new() -> Result<Self, io::Error> {
|
||||
let devurandom = File::open(DEV_URANDOM)?;
|
||||
|
||||
Ok(Self { devurandom })
|
||||
}
|
||||
|
||||
/// Fills `buf` with bytes from `/dev/urandom`.
|
||||
///
|
||||
/// This is equivalent to calling [`Read::read_exact`] on `OsRandom`, but it
|
||||
/// keeps the common "fill this byte slice" use case available without
|
||||
/// importing the [`Read`] trait.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns any I/O error produced while reading from `/dev/urandom`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// let mut rng = ez_urandom::OsRandom::try_new()?;
|
||||
/// let mut bytes = [0u8; 32];
|
||||
/// rng.fill_bytes(&mut bytes)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn fill_bytes(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
self.devurandom.read_exact(buf)
|
||||
}
|
||||
|
||||
os_random_get_integer_impls!(
|
||||
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
|
||||
);
|
||||
}
|
||||
|
||||
/// Opens `/dev/urandom` and panics if the operating-system source is
|
||||
/// unavailable.
|
||||
///
|
||||
/// Prefer [`OsRandom::try_new`] when the caller should decide how to handle an
|
||||
/// I/O failure.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut rng = ez_urandom::OsRandom::default();
|
||||
/// let mut bytes = [0u8; 16];
|
||||
/// rng.fill_bytes(&mut bytes).expect("/dev/urandom read failed");
|
||||
/// ```
|
||||
impl Default for OsRandom {
|
||||
fn default() -> Self {
|
||||
Self::try_new().expect("/dev/urandom should be available")
|
||||
}
|
||||
}
|
||||
|
||||
/// Uniform sampling helpers.
|
||||
///
|
||||
/// All methods here use rejection sampling on top of [`OsRandom::get_u32`]
|
||||
|
||||
Reference in New Issue
Block a user