Skip to main content

hpx_emulation/emulation/
rand.rs

1use std::{
2    cell::Cell,
3    collections::hash_map::RandomState,
4    hash::{BuildHasher, Hasher},
5    num::Wrapping,
6};
7
8use strum::VariantArray;
9
10use super::{Emulation, EmulationOS, EmulationOption};
11
12// from: https://github.com/seanmonstar/reqwest/blob/44ac897f1ab35ba24a195927043d185d5cbb6912/src/util.rs#L27
13fn fast_random() -> u64 {
14    thread_local! {
15        static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(seed()));
16    }
17
18    #[inline]
19    fn seed() -> u64 {
20        let seed = RandomState::new();
21
22        let mut out = 0;
23        let mut cnt = 0;
24        while out == 0 {
25            cnt += 1;
26            let mut hasher = seed.build_hasher();
27            hasher.write_usize(cnt);
28            out = hasher.finish();
29        }
30        out
31    }
32
33    RNG.with(|rng| {
34        let mut n = rng.get();
35        debug_assert_ne!(n.0, 0);
36        n ^= n >> 12;
37        n ^= n << 25;
38        n ^= n >> 27;
39        rng.set(n);
40        n.0.wrapping_mul(0x2545_f491_4f6c_dd1d)
41    })
42}
43
44impl Emulation {
45    /// Returns a random variant of the `Emulation` enum.
46    ///
47    /// This method uses a fast random number generator to select a random variant
48    /// from the `Emulation::VARIANTS` array. The random number generator is based
49    /// on the XOR-Shift algorithm, which is efficient and suitable for use in
50    /// multi-threaded environments.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use hpx_emulation::Emulation;
56    ///
57    /// let random_emulation = Emulation::random();
58    /// println!("{:?}", random_emulation);
59    /// ```
60    ///
61    /// # Panics
62    ///
63    /// This method will panic if the `Emulation::VARIANTS` array is empty.
64    #[inline]
65    pub fn random() -> EmulationOption {
66        let emulation = Emulation::VARIANTS;
67        let emulation_os = EmulationOS::VARIANTS;
68        let rand = fast_random() as usize;
69        EmulationOption::builder()
70            .emulation(emulation[rand % emulation.len()])
71            .emulation_os(emulation_os[rand % emulation_os.len()])
72            .build()
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use std::{sync::Arc, thread};
79
80    use parking_lot::Mutex;
81
82    use super::*;
83
84    #[test]
85    fn test_concurrent_get_random_emulation() {
86        const THREAD_COUNT: usize = 10;
87        const ITERATIONS: usize = 100;
88
89        let results = Arc::new(Mutex::new(Vec::new()));
90
91        let mut handles = vec![];
92
93        for _ in 0..THREAD_COUNT {
94            let results = Arc::clone(&results);
95            let handle = thread::spawn(move || {
96                for _ in 0..ITERATIONS {
97                    let emulation = Emulation::random();
98                    let mut results = results.lock();
99                    results.push(emulation);
100                }
101            });
102            handles.push(handle);
103        }
104
105        for handle in handles {
106            handle.join().expect("worker thread panicked");
107        }
108
109        let results = results.lock();
110        println!("Total results: {}", results.len());
111    }
112}