Skip to main content

ax_rnd/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub mod rng;
4pub mod rng_alphanumeric;
5pub use rng::AxRng;
6
7#[cfg(feature = "std")]
8use std::cell::Cell;
9
10#[cfg(feature = "std")]
11thread_local! {
12    static THREAD_RNG: Cell<AxRng> = Cell::new(AxRng::new({
13        use std::time::{SystemTime, UNIX_EPOCH};
14        SystemTime::now()
15            .duration_since(UNIX_EPOCH)
16            .unwrap_or_else(|_| std::time::Duration::from_nanos(0xA0761D6478BD642F))
17            .as_nanos() as u64
18    }));
19}
20
21// Convenience API like fastrand - uses thread-local RNG with default seed
22#[cfg(feature = "std")]
23#[inline(always)]
24pub fn u8() -> u8 {
25    THREAD_RNG.with(|rng| {
26        let mut r = rng.get();
27        let v = r.next_u8();
28        rng.set(r);
29        v
30    })
31}
32
33#[cfg(feature = "std")]
34#[inline(always)]
35pub fn u16() -> u16 {
36    THREAD_RNG.with(|rng| {
37        let mut r = rng.get();
38        let v = r.next_u16();
39        rng.set(r);
40        v
41    })
42}
43
44#[cfg(feature = "std")]
45#[inline(always)]
46pub fn u32() -> u32 {
47    THREAD_RNG.with(|rng| {
48        let mut r = rng.get();
49        let v = r.next_u32();
50        rng.set(r);
51        v
52    })
53}
54
55#[cfg(feature = "std")]
56#[inline(always)]
57pub fn u64() -> u64 {
58    THREAD_RNG.with(|rng| {
59        let mut r = rng.get();
60        let v = r.next_u64();
61        rng.set(r);
62        v
63    })
64}
65
66#[cfg(feature = "std")]
67#[inline(always)]
68pub fn bool() -> bool {
69    THREAD_RNG.with(|rng| {
70        let mut r = rng.get();
71        let v = r.next_bool();
72        rng.set(r);
73        v
74    })
75}
76
77#[cfg(feature = "std")]
78#[inline(always)]
79pub fn f32() -> f32 {
80    THREAD_RNG.with(|rng| {
81        let mut r = rng.get();
82        let v = r.next_f32();
83        rng.set(r);
84        v
85    })
86}
87
88#[cfg(feature = "std")]
89#[inline(always)]
90pub fn f64() -> f64 {
91    THREAD_RNG.with(|rng| {
92        let mut r = rng.get();
93        let v = r.next_f64();
94        rng.set(r);
95        v
96    })
97}
98
99#[cfg(feature = "std")]
100#[inline(always)]
101pub fn alphanumeric(len: usize) -> String {
102    THREAD_RNG.with(|rng| {
103        let mut r = rng.get();
104        let v = r.next_alphanumeric(len);
105        rng.set(r);
106        v
107    })
108}
109
110#[cfg(feature = "std")]
111#[inline(always)]
112pub fn base64url(len: usize) -> String {
113    THREAD_RNG.with(|rng| {
114        let mut r = rng.get();
115        let v = r.next_base64url(len);
116        rng.set(r);
117        v
118    })
119}
120
121#[cfg(feature = "std")]
122#[inline(always)]
123pub fn fill(out: &mut [u8]) {
124    THREAD_RNG.with(|rng| {
125        let mut r = rng.get();
126        r.fill_bytes(out);
127        rng.set(r);
128    })
129}
130
131#[cfg(feature = "std")]
132#[inline(always)]
133pub fn with_rng<F, R>(f: F) -> R
134where
135    F: FnOnce(&mut AxRng) -> R,
136{
137    THREAD_RNG.with(|rng| {
138        let mut r = rng.get();
139        let result = f(&mut r);
140        rng.set(r);
141        result
142    })
143}
144
145#[inline]
146pub fn rng(seed: u64) -> AxRng {
147    AxRng::new(seed)
148}
149
150#[inline(always)]
151pub fn fill_bytes(rng: &mut AxRng, out: &mut [u8]) {
152    rng::fill_bytes(rng, out)
153}
154
155#[inline(always)]
156pub fn fill_u64(rng: &mut AxRng, out: &mut [u64]) {
157    rng::fill_u64(rng, out)
158}
159
160#[inline(always)]
161pub fn fill_u32(rng: &mut AxRng, out: &mut [u32]) {
162    rng::fill_u32(rng, out)
163}
164
165#[inline(always)]
166pub fn random_u64(seed: u64) -> u64 {
167    let mut rnd = AxRng::new(seed);
168
169    rnd.next_u64()
170}
171
172#[inline(always)]
173pub fn random_u32(seed: u64) -> u32 {
174    let mut rnd = AxRng::new(seed);
175
176    rnd.next_u32()
177}
178
179#[inline(always)]
180pub fn random_bool(seed: u64) -> bool {
181    let mut rnd = AxRng::new(seed);
182
183    rnd.next_bool()
184}
185
186#[inline(always)]
187pub fn random_f64(seed: u64) -> f64 {
188    let mut rnd = AxRng::new(seed);
189
190    rnd.next_f64()
191}
192
193#[inline(always)]
194pub fn bounded_u64(seed: u64, upper: u64) -> u64 {
195    assert!(upper > 0, "upper must be > 0");
196
197    let mut rnd = AxRng::new(seed);
198
199    rnd.bounded_u64(upper)
200}
201
202#[inline(always)]
203pub fn random_alphanumeric(seed: u64, len: usize) -> String {
204    let mut rnd = AxRng::new(seed);
205    rnd.next_alphanumeric(len)
206}
207
208#[inline(always)]
209pub fn alpha(seed: u64, len: usize) -> String {
210    let mut rnd = AxRng::new(seed);
211    rnd.alpha(len)
212}
213
214#[inline(always)]
215pub fn random_base64url(seed: u64, len: usize) -> String {
216    let mut rnd = AxRng::new(seed);
217    rnd.next_base64url(len)
218}
219
220#[inline(always)]
221pub fn token(seed: u64, len: usize) -> String {
222    let mut rnd = AxRng::new(seed);
223    rnd.token(len)
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229
230    #[test]
231    fn deterministic() {
232        let mut a = rng(123);
233        let mut b = rng(123);
234
235        for _ in 0..1000 {
236            assert_eq!(a.next_u64(), b.next_u64(),);
237        }
238    }
239
240    #[test]
241    fn fill_bytes_works() {
242        let mut rng = rng(999);
243
244        let mut buf = [0u8; 128];
245
246        fill_bytes(&mut rng, &mut buf);
247
248        let mut zero = true;
249
250        for b in buf {
251            if b != 0 {
252                zero = false;
253                break;
254            }
255        }
256
257        assert!(!zero);
258    }
259
260    #[test]
261    fn split_is_different() {
262        let mut a = rng(42);
263
264        let mut b = a.split();
265
266        assert_ne!(a.next_u64(), b.next_u64(),);
267    }
268
269    #[test]
270    fn bounded_range() {
271        let mut rng = rng(123);
272
273        for _ in 0..10000 {
274            let v = rng.bounded_u64(10);
275
276            assert!(v < 10);
277        }
278    }
279
280    #[cfg(feature = "std")]
281    #[test]
282    fn convenience_api_u8() {
283        let v = u8();
284        assert!(v > 0 || true); // Just ensure it runs
285    }
286
287    #[cfg(feature = "std")]
288    #[test]
289    fn convenience_api_u16() {
290        let v = u16();
291        assert!(v > 0 || true);
292    }
293
294    #[cfg(feature = "std")]
295    #[test]
296    fn convenience_api_u32() {
297        let v = u32();
298        assert!(v > 0 || true);
299    }
300
301    #[cfg(feature = "std")]
302    #[test]
303    fn convenience_api_u64() {
304        let v = u64();
305        assert!(v > 0 || true);
306    }
307
308    #[cfg(feature = "std")]
309    #[test]
310    fn convenience_api_bool() {
311        let v = bool();
312        assert!(v == true || v == false);
313    }
314
315    #[cfg(feature = "std")]
316    #[test]
317    fn convenience_api_f32() {
318        let v = f32();
319        assert!(v >= 0.0 && v < 1.0);
320    }
321
322    #[cfg(feature = "std")]
323    #[test]
324    fn convenience_api_f64() {
325        let v = f64();
326        assert!(v >= 0.0 && v < 1.0);
327    }
328
329    #[cfg(feature = "std")]
330    #[test]
331    fn convenience_api_alphanumeric() {
332        let s = alphanumeric(32);
333        assert_eq!(s.len(), 32);
334        assert!(s.chars().all(|c| c.is_ascii_alphanumeric()));
335    }
336
337    #[cfg(feature = "std")]
338    #[test]
339    fn convenience_api_base64url() {
340        let s = base64url(32);
341        assert_eq!(s.len(), 32);
342        assert!(
343            s.chars()
344                .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
345        );
346    }
347
348    #[cfg(feature = "std")]
349    #[test]
350    fn convenience_api_fill() {
351        let mut buf = [0u8; 64];
352        fill(&mut buf);
353        let mut all_zero = true;
354        for b in buf {
355            if b != 0 {
356                all_zero = false;
357                break;
358            }
359        }
360        assert!(!all_zero);
361    }
362
363    #[cfg(feature = "std")]
364    #[test]
365    fn convenience_api_with_rng() {
366        let v = with_rng(|rng| rng.next_u64());
367        assert!(v > 0 || true);
368    }
369}