o192 0.2.2

ORION-192: ordered, resilient, independent, URL-safe 192-bit IDs for distributed systems.
Documentation
//! Pooled CSPRNG used by [`OrionIdGenerator`](crate::OrionIdGenerator).
//!
//! The OS CSPRNG (`getrandom::getrandom`) is the conforming source of
//! randomness; this module wraps it in a refillable byte pool to
//! amortise the per-call syscall overhead.

use crate::error::OrionIdError;

/// Function pointer signature for an injectable CSPRNG source.
///
/// Implementations MUST fill the entire slice with cryptographically
/// secure random bytes and return `Ok(())` on success. The test suite
/// uses a deterministic fake for some scenarios; production code
/// should always use [`default_random_fill`].
pub(crate) type RandomFill = fn(&mut [u8]) -> Result<(), OrionIdError>;

/// Default [`RandomFill`]: a thin wrapper around `getrandom::getrandom`.
pub(crate) fn default_random_fill(out: &mut [u8]) -> Result<(), OrionIdError> {
    getrandom::getrandom(out).map_err(|_| OrionIdError::RandomFailure)
}

/// A fixed-size byte pool that lazily refills itself from a CSPRNG.
///
/// The pool is **not** thread-safe; each [`OrionIdGenerator`] owns
/// its own pool, and the generator itself is `Send` but not `Sync`.
pub(crate) struct RandomPool {
    buffer: Vec<u8>,
    cursor: usize,
    fill: RandomFill,
}

impl RandomPool {
    pub(crate) fn new(size_bytes: usize, fill: RandomFill) -> Self {
        let buffer = vec![0u8; size_bytes];
        let cursor = size_bytes; // empty → refill on first use
        Self {
            buffer,
            cursor,
            fill,
        }
    }

    pub(crate) fn fill_into(&mut self, out: &mut [u8]) -> Result<(), OrionIdError> {
        let length = out.len();
        if length > self.buffer.len() {
            return (self.fill)(out);
        }

        if self.cursor + length > self.buffer.len() {
            self.refill()?;
        }

        let start = self.cursor;
        let end = start + length;
        out.copy_from_slice(&self.buffer[start..end]);
        self.cursor = end;
        Ok(())
    }

    pub(crate) fn next_10_bits(&mut self) -> Result<u32, OrionIdError> {
        let mut tmp = [0u8; 2];
        self.fill_into(&mut tmp)?;
        Ok((((u32::from(tmp[0])) << 8) | u32::from(tmp[1])) & 0x03ff)
    }

    fn refill(&mut self) -> Result<(), OrionIdError> {
        (self.fill)(&mut self.buffer)?;
        self.cursor = 0;
        Ok(())
    }
}