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:
@@ -0,0 +1,14 @@
|
|||||||
|
//! Run with: `cargo run --example bytes`
|
||||||
|
|
||||||
|
use ez_urandom::OsRandom;
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
let mut rng = OsRandom::default();
|
||||||
|
|
||||||
|
let mut session_key = [0u8; 32];
|
||||||
|
rng.fill_bytes(&mut session_key)?;
|
||||||
|
|
||||||
|
println!("session key bytes: {session_key:02x?}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+63
@@ -4,6 +4,16 @@
|
|||||||
//! `/dev/urandom` with helpers for reading primitive integers, sampling
|
//! `/dev/urandom` with helpers for reading primitive integers, sampling
|
||||||
//! uniformly without modulo bias, and generating random ASCII strings from a
|
//! uniformly without modulo bias, and generating random ASCII strings from a
|
||||||
//! caller-supplied alphabet. Common alphabets live in [`charset`].
|
//! 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::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
@@ -102,17 +112,70 @@ macro_rules! os_random_get_integer_impls {
|
|||||||
impl OsRandom {
|
impl OsRandom {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns any I/O error produced while opening `/dev/urandom`.
|
/// 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> {
|
pub fn try_new() -> Result<Self, io::Error> {
|
||||||
let devurandom = File::open(DEV_URANDOM)?;
|
let devurandom = File::open(DEV_URANDOM)?;
|
||||||
|
|
||||||
Ok(Self { devurandom })
|
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!(
|
os_random_get_integer_impls!(
|
||||||
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
|
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.
|
/// Uniform sampling helpers.
|
||||||
///
|
///
|
||||||
/// All methods here use rejection sampling on top of [`OsRandom::get_u32`]
|
/// All methods here use rejection sampling on top of [`OsRandom::get_u32`]
|
||||||
|
|||||||
Reference in New Issue
Block a user