rquest_util/emulation/
rand.rs

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