1use core::ops::RangeInclusive;
5
6pub trait Generator: 'static + Send {
12 fn public_random_fill(&mut self, dest: &mut [u8]);
15
16 fn private_random_fill(&mut self, dest: &mut [u8]);
19}
20
21pub(crate) fn gen_range_biased<R: Generator + ?Sized>(
27 random_generator: &mut R,
28 range: RangeInclusive<usize>,
29) -> usize {
30 if range.start() == range.end() {
31 return *range.start();
32 }
33
34 let mut dest = [0; core::mem::size_of::<usize>()];
35 random_generator.public_random_fill(&mut dest);
36 let result = usize::from_le_bytes(dest);
37
38 let max_variance = (range.end() - range.start()).saturating_add(1);
39 range.start() + result % max_variance
40}
41
42#[cfg(any(test, feature = "testing"))]
43pub mod testing {
44 use crate::random;
45
46 #[derive(Debug, Default)]
47 pub struct Generator(pub u8);
48
49 impl random::Generator for Generator {
50 fn public_random_fill(&mut self, dest: &mut [u8]) {
51 let seed = self.0;
52
53 for (i, elem) in dest.iter_mut().enumerate() {
54 *elem = seed ^ i as u8;
55 }
56
57 self.0 = self.0.wrapping_add(1)
58 }
59
60 fn private_random_fill(&mut self, dest: &mut [u8]) {
61 let seed = u8::MAX - self.0;
62
63 for (i, elem) in dest.iter_mut().enumerate() {
64 *elem = seed ^ i as u8;
65 }
66
67 self.0 = self.0.wrapping_add(1)
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use crate::random;
75
76 #[test]
77 #[cfg_attr(miri, ignore)] #[cfg_attr(kani, kani::proof, kani::unwind(10), kani::solver(kissat))]
79 fn gen_range_biased_test() {
80 bolero::check!()
81 .with_type()
82 .cloned()
83 .for_each(|(seed, mut min, mut max)| {
84 if min > max {
85 core::mem::swap(&mut min, &mut max);
86 }
87 let mut generator = random::testing::Generator(seed);
88 let result = random::gen_range_biased(&mut generator, min..=max);
89 assert!(result >= min);
90 assert!(result <= max);
91 });
92 }
93}