Add the MIT No Attribution license text and mark the crate with the
matching SPDX identifier. This makes the repository license explicit in
both the source header and Cargo package metadata, so downstream tools
can recognize the crate as MIT-0 without guessing from prose.
The license file uses the SPDX MIT-0 text with this project's copyright
holder. The Rust source keeps the machine-readable SPDX-License-Identifier
header at the top of the crate root.
Test Plan:
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt
Trailer:
- SPDX-License-Identifier: MIT-0
- SPDX Specification: https://spdx.github.io/spdx-spec/v3.0.1/
- SPDX MIT-0: https://spdx.org/licenses/MIT-0.html
Add OsRandom::try_clone for fallible duplication of the underlying
/dev/urandom file handle, and implement Clone as the infallible convenience
form. This gives independent call sites their own OsRandom values without
adding interior mutability or shared buffering to the core type.
The fallible method is documented as the preferred API when callers need to
handle a failed file-handle duplication. Clone mirrors Default by panicking only
for the convenience path.
Document both cloning forms with doctested examples and add a runnable clone
example that uses independent handles for IDs and string helpers.
Test Plan:
- cargo test
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt
Refs: IDEAS.md ergonomics backlog
Add token, hex, and pin helpers for the common ASCII string formats already
represented by the crate's built-in charsets. These methods keep frequent use
cases concise while continuing to route through string_from, so callers still
get the same uniform per-character sampling behavior.
Avoid adding a password preset in this change. Password generation implies
policy choices around symbols, ambiguous characters, and service-specific
constraints, while these presets are direct names for existing alphabets.
Document every preset with doctested examples and add a runnable presets
example. Update the demo to show both presets and custom string generation.
Test Plan:
- cargo test
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt
Refs: IDEAS.md ergonomics backlog
Add OsRandom::choose for callers that want a uniformly selected reference from
any non-empty slice. This covers the common generic selection use case without
adding collection algorithms such as shuffle.
Keep OsRandom::pick as the byte-oriented helper by delegating it through
choose. This preserves the existing public API while sharing the usize range
sampler and avoiding the old u32 length limit.
Document choose, pick, and string_from with working examples, and add a choices
example that demonstrates generic slice selection alongside byte alphabets.
Test Plan:
- cargo test
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt
Refs: IDEAS.md ergonomics backlog
Expand range sampling from the original u32-only helper to all primitive
integer widths. The new gen_range_<int> methods cover 0..n sampling, while
matching gen_range_<int>_in methods accept standard RangeBounds forms such as
exclusive, inclusive, open-ended, and full-width ranges.
Use rejection sampling against the matching primitive reader for each integer
width. That keeps modulo bias out of the public helpers without introducing a
general RNG trait or depending on rand. Full-width ranges fall back directly to
the primitive reader because their span cannot be represented in the same
integer type.
Document the generated APIs with doctested examples and add a ranges example
that demonstrates unsigned, signed, usize, and full-width sampling.
Test Plan:
- cargo test
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt
Refs: IDEAS.md ergonomics backlog
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
A small, dependency-light crate that wraps `/dev/urandom` and exposes
ergonomic helpers for the things people actually reach for randomness
to do: read a primitive integer, sample a number in a half-open range,
or generate a random ASCII string from a given alphabet.
Why this crate exists
---------------------
The Rust ecosystem already has `rand` and `getrandom`, but both bring
in more surface area than is warranted when all you need is "give me
some bytes from the OS." This crate intentionally stays tiny: a single
`File` handle to `/dev/urandom`, a handful of typed readers, and one
rejection-sampling helper. No traits to learn, no generic plumbing, no
algorithmic CSPRNGs in-process — the kernel does that job already.
What's included
---------------
- `OsRandom`: thin wrapper around an open `/dev/urandom` handle,
constructed via `try_new()`. Implements `io::Read` so it can plug
into anywhere a byte source is expected.
- Typed integer readers (`get_u8` ... `get_i128`, plus `usize`/`isize`)
generated by a small `paste!` macro. Each reads exactly
`size_of::<T>()` bytes and interprets them in native-endian order so
every bit pattern is equally likely.
- `gen_range_u32(n)`: uniform integer in `0..n` via rejection sampling
on top of `get_u32`. The cutoff is computed as the largest multiple
of `n` that fits in `2^32`, so there is *no* modulo bias — values at
or above the cutoff are discarded and resampled. This is the
textbook fix for the "`rand() % n` is biased when `n` doesn't divide
`RAND_MAX+1`" footgun.
- `pick(set)` and `string_from(set, len)`: uniform byte / ASCII string
drawn from a caller-supplied alphabet, layered on `gen_range_u32`.
- `charset` module with `const`-built alphabets: `DIGITS`, `LOWERCASE`,
`UPPERCASE`, `ALPHABETIC`, `ALPHANUMERIC`, `HEX_LOWER`, `HEX_UPPER`.
Built with a small `const fn concat` so the arrays exist as compile-
time constants with no runtime allocation.
Design choices and tradeoffs
----------------------------
- Linux/Unix only by construction: opens `/dev/urandom` directly. This
is a deliberate scope limit — supporting Windows would mean pulling
in `BCryptGenRandom` and an abstraction layer, which defeats the
point of the crate. Users who need cross-platform should reach for
`getrandom`.
- `unsafe_code = "forbid"` at the crate level. The implementation does
not need `unsafe`, and forbidding it makes that contract explicit
and machine-checked.
- Clippy `pedantic` is on as `warn`, plus `unwrap_used = "warn"` and
`todo = "warn"`, so the code is held to a tighter standard than the
defaults from day one.
- `panic = "unwind"` in release: the crate's `assert!`s (e.g. "set
must not be empty") should be recoverable by callers that wrap
them, not abort the process.
- Native-endian integer decoding: the bits are uniformly random, so
endianness is irrelevant to the distribution. Choosing native-endian
avoids a needless byte swap on every read.
- Rejection sampling uses a 32-bit cutoff regardless of the requested
range. A 64-bit cutoff would reduce the rejection probability for
ranges close to `u32::MAX`, but in practice the worst-case rejection
rate is < 50% and the simpler code wins.
Known limitations
-----------------
- No async API; reads are blocking. `/dev/urandom` does not block in
practice on Linux post-init, so this is fine for typical use.
- `gen_range` is only provided for `u32`. Wider ranges would need a
64-bit variant; not added until a use case appears.
- File handle is held for the lifetime of `OsRandom`. Callers that
want a fresh fd per call should construct a new instance.
Test Plan
---------
- `cargo build` and `cargo clippy --all-targets` are clean under the
pedantic lint set configured in `Cargo.toml`.
- Manual smoke test by reading several integers and generating
alphanumeric / hex / digit strings; values are well-distributed and
no obvious bias is visible.