From 733a2508cb0ce22cbe934bae36d6e8e36c40b0e1 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Tue, 28 Apr 2026 19:58:27 +0200 Subject: [PATCH] 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 --- examples/bytes.rs | 14 +++++++++++ src/lib.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 examples/bytes.rs diff --git a/examples/bytes.rs b/examples/bytes.rs new file mode 100644 index 0000000..4e2c5f3 --- /dev/null +++ b/examples/bytes.rs @@ -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(()) +} diff --git a/src/lib.rs b/src/lib.rs index 587ddf3..e7d6d00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { 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`]