wreq_util/emulation/
rand.rs1use 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
12fn 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 #[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::{
79 sync::{Arc, Mutex},
80 thread,
81 };
82
83 use super::*;
84
85 #[test]
86 fn test_concurrent_get_random_emulation() {
87 const THREAD_COUNT: usize = 10;
88 const ITERATIONS: usize = 100;
89
90 let results = Arc::new(Mutex::new(Vec::new()));
91
92 let mut handles = vec![];
93
94 for _ in 0..THREAD_COUNT {
95 let results = Arc::clone(&results);
96 let handle = thread::spawn(move || {
97 for _ in 0..ITERATIONS {
98 let emulation = Emulation::random();
99 let mut results = results.lock().unwrap();
100 results.push(emulation);
101 }
102 });
103 handles.push(handle);
104 }
105
106 for handle in handles {
107 handle.join().unwrap();
108 }
109
110 let results = results.lock().unwrap();
111 println!("Total results: {}", results.len());
112 }
113}