Skip to main content

reifydb_runtime/context/rng/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4//! Random number generation abstraction.
5//!
6//! Provides an `Rng` enum that can use either OS entropy or a deterministic seed.
7
8use std::sync::{Arc, Mutex};
9
10use getrandom::fill as getrandom_fill;
11use rand::{Rng as RandRng, SeedableRng, rngs::StdRng};
12
13/// A random number generator that can be either OS-backed or seeded/deterministic.
14#[derive(Clone, Default)]
15pub enum Rng {
16	/// Uses OS entropy via `getrandom` (non-deterministic).
17	#[default]
18	Os,
19	/// Uses a seeded PRNG for deterministic output (e.g. testing).
20	Seeded(SeededRng),
21}
22
23impl Rng {
24	/// Create a deterministic RNG from the given seed.
25	pub fn seeded(seed: u64) -> Self {
26		Rng::Seeded(SeededRng::new(seed))
27	}
28
29	/// Generate 16 random bytes (suitable for UUID v4).
30	pub fn bytes_16(&self) -> [u8; 16] {
31		match self {
32			Rng::Os => {
33				let mut buf = [0u8; 16];
34				getrandom_fill(&mut buf).expect("getrandom failed");
35				buf
36			}
37			Rng::Seeded(seeded) => {
38				let mut buf = [0u8; 16];
39				let mut rng = seeded.inner.lock().unwrap();
40				rng.fill_bytes(&mut buf);
41				buf
42			}
43		}
44	}
45
46	/// Generate 32 random bytes (suitable for token generation).
47	pub fn bytes_32(&self) -> [u8; 32] {
48		match self {
49			Rng::Os => {
50				let mut buf = [0u8; 32];
51				getrandom_fill(&mut buf).expect("getrandom failed");
52				buf
53			}
54			Rng::Seeded(seeded) => {
55				let mut buf = [0u8; 32];
56				let mut rng = seeded.inner.lock().unwrap();
57				rng.fill_bytes(&mut buf);
58				buf
59			}
60		}
61	}
62
63	/// Generate 10 random bytes (suitable for UUID v7 random portion).
64	pub fn bytes_10(&self) -> [u8; 10] {
65		match self {
66			Rng::Os => {
67				let mut buf = [0u8; 10];
68				getrandom_fill(&mut buf).expect("getrandom failed");
69				buf
70			}
71			Rng::Seeded(seeded) => {
72				let mut buf = [0u8; 10];
73				let mut rng = seeded.inner.lock().unwrap();
74				rng.fill_bytes(&mut buf);
75				buf
76			}
77		}
78	}
79
80	/// Generate 10 random bytes from the infrastructure RNG stream.
81	///
82	/// Uses a separate RNG stream so that infrastructure operations (like
83	/// transaction ID generation) do not perturb the primary RNG state.
84	/// This ensures deterministic test output regardless of how many internal
85	/// transactions each test runner creates.
86	pub fn infra_bytes_10(&self) -> [u8; 10] {
87		match self {
88			Rng::Os => {
89				let mut buf = [0u8; 10];
90				getrandom_fill(&mut buf).expect("getrandom failed");
91				buf
92			}
93			Rng::Seeded(seeded) => {
94				let mut buf = [0u8; 10];
95				let mut rng = seeded.infra.lock().unwrap();
96				rng.fill_bytes(&mut buf);
97				buf
98			}
99		}
100	}
101
102	/// Generate 32 random bytes from the infrastructure RNG stream.
103	///
104	/// Uses a separate RNG stream for infrastructure operations (like
105	/// session token generation) that should not affect deterministic
106	/// test output.
107	pub fn infra_bytes_32(&self) -> [u8; 32] {
108		match self {
109			Rng::Os => {
110				let mut buf = [0u8; 32];
111				getrandom_fill(&mut buf).expect("getrandom failed");
112				buf
113			}
114			Rng::Seeded(seeded) => {
115				let mut buf = [0u8; 32];
116				let mut rng = seeded.infra.lock().unwrap();
117				rng.fill_bytes(&mut buf);
118				buf
119			}
120		}
121	}
122}
123
124/// A seeded, deterministic RNG backed by `StdRng` wrapped in `Arc<Mutex<..>>`.
125#[derive(Clone)]
126pub struct SeededRng {
127	inner: Arc<Mutex<StdRng>>,
128	/// Separate RNG stream for infrastructure use (e.g. transaction IDs).
129	infra: Arc<Mutex<StdRng>>,
130}
131
132impl SeededRng {
133	/// Create a new seeded RNG from the given seed.
134	pub fn new(seed: u64) -> Self {
135		Self {
136			inner: Arc::new(Mutex::new(StdRng::seed_from_u64(seed))),
137			infra: Arc::new(Mutex::new(StdRng::seed_from_u64(seed ^ 0x5A5A5A5A5A5A5A5A))),
138		}
139	}
140}