xref: /drstd/dlibc/src/unix/header/stdlib/random.rs (revision 9670759b785600bf6315e4173e46a602f16add7a)
1 //! Helper functions for random() and friends, see https://pubs.opengroup.org/onlinepubs/7908799/xsh/initstate.html
2 /* Ported from musl's implementation (src/prng/random.c). Does not
3  * currently implement locking, though. */
4 
5 
6 use core::{convert::TryFrom, ptr};
7 
8 #[rustfmt::skip]
9 static mut X_INIT: [u32; 32] = [
10     0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646,
11     0xc7033129, 0x30705b04, 0x20fd5db4, 0x9a8b7f78,
12     0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff,
13     0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc,
14     0x8ae16fd9, 0x742d2f7a, 0x0d1f0796, 0x76035e09,
15     0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74,
16     0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115,
17     0x0cab8628, 0xf043bfa4, 0x398150e9, 0x37521657,
18 ];
19 
20 /* N needs to accommodate values up to 63, corresponding to the maximum
21  * state array size of 256 bytes. I and J must be able to accommodate
22  * values less than or equal to N. */
23 pub static mut N: u8 = 31;
24 pub static mut I: u8 = 3;
25 pub static mut J: u8 = 0;
26 
27 /* As such, random() and related functions work on u32 values, but POSIX
28  * allows the user to supply a custom state data array as a `char *`
29  * with no requirements on alignment. Thus, we must assume the worst in
30  * terms of alignment and convert back and forth from [u8; 4].
31  *
32  * Also, unlike in C, we can't take the address of the initializing
33  * array outside of a function. */
34 pub static mut X_PTR: *mut [u8; 4] = ptr::null_mut();
35 
36 // To be called in any function that may read from X_PTR
37 pub unsafe fn ensure_x_ptr_init() {
38     if X_PTR.is_null() {
39         let x_u32_ptr: *mut u32 = &mut X_INIT[1];
40         X_PTR = x_u32_ptr.cast::<[u8; 4]>();
41     }
42 }
43 
44 pub fn lcg31_step(x: u32) -> u32 {
45     1103515245_u32.wrapping_mul(x).wrapping_add(12345_u32) & 0x7fffffff
46 }
47 
48 pub fn lcg64_step(x: u64) -> u64 {
49     6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64)
50 }
51 
52 pub unsafe fn save_state() -> *mut [u8; 4] {
53     ensure_x_ptr_init();
54 
55     let stash_value: u32 = (u32::from(N) << 16) | (u32::from(I) << 8) | u32::from(J);
56     *X_PTR.offset(-1) = stash_value.to_ne_bytes();
57     X_PTR.offset(-1)
58 }
59 
60 pub unsafe fn load_state(state_ptr: *mut [u8; 4]) {
61     let stash_value = u32::from_ne_bytes(*state_ptr);
62     X_PTR = state_ptr.offset(1);
63 
64     /* This calculation of N does not have a bit mask in the musl
65      * original, in principle resulting in a u16, but obtaining a value
66      * larger than 63 can probably be dismissed as pathological. */
67     N = u8::try_from((stash_value >> 16) & 0xff).unwrap();
68 
69     // I and J calculations are straight from musl
70     I = u8::try_from((stash_value >> 8) & 0xff).unwrap();
71     J = u8::try_from(stash_value & 0xff).unwrap();
72 }
73 
74 pub unsafe fn seed(seed: ::c_uint) {
75     ensure_x_ptr_init();
76 
77     let mut s = seed as u64;
78 
79     if N == 0 {
80         *X_PTR = (s as u32).to_ne_bytes();
81     } else {
82         I = if N == 31 || N == 7 { 3 } else { 1 };
83 
84         J = 0;
85 
86         for k in 0..usize::from(N) {
87             s = lcg64_step(s);
88 
89             // Conversion will always succeed (value is a 32-bit right-
90             // shift of a 64-bit integer).
91             *X_PTR.add(k) = u32::try_from(s >> 32).unwrap().to_ne_bytes();
92         }
93 
94         // ensure X contains at least one odd number
95         *X_PTR = (u32::from_ne_bytes(*X_PTR) | 1).to_ne_bytes();
96     }
97 }
98