rquest_util/emulation/
rand.rs1use 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
9fn 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 #[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}