wreq_util/emulation/
rand.rs

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