@@ -40,6 +40,74 @@ pub const N: u32 = if cfg!(target_arch = "x86_64") && !cfg!(debug_assertions) {
40
40
10_000
41
41
} ;
42
42
43
+ trait FuzzInt : MinInt {
44
+ /// LUT used for maximizing the space covered and minimizing the computational cost of fuzzing
45
+ /// in `builtins-test`. For example, Self = u128 produces [0,1,2,7,8,15,16,31,32,63,64,95,96,
46
+ /// 111,112,119,120,125,126,127].
47
+ const FUZZ_LENGTHS : [ u8 ; 20 ] = make_fuzz_lengths ( <Self as MinInt >:: BITS ) ;
48
+
49
+ /// The number of entries of `FUZZ_LENGTHS` actually used. The maximum is 20 for u128.
50
+ const FUZZ_NUM : usize = {
51
+ let log2 = ( <Self as MinInt >:: BITS - 1 ) . count_ones ( ) as usize ;
52
+ if log2 == 3 {
53
+ // case for u8
54
+ 6
55
+ } else {
56
+ // 3 entries on each extreme, 2 in the middle, and 4 for each scale of intermediate
57
+ // boundaries.
58
+ 8 + ( 4 * ( log2 - 4 ) )
59
+ }
60
+ } ;
61
+ }
62
+
63
+ impl < I > FuzzInt for I where I : MinInt { }
64
+
65
+ const fn make_fuzz_lengths ( bits : u32 ) -> [ u8 ; 20 ] {
66
+ let mut v = [ 0u8 ; 20 ] ;
67
+ v[ 0 ] = 0 ;
68
+ v[ 1 ] = 1 ;
69
+ v[ 2 ] = 2 ; // important for parity and the iX::MIN case when reversed
70
+ let mut i = 3 ;
71
+
72
+ // No need for any more until the byte boundary, because there should be no algorithms
73
+ // that are sensitive to anything not next to byte boundaries after 2. We also scale
74
+ // in powers of two, which is important to prevent u128 corner tests from getting too
75
+ // big.
76
+ let mut l = 8 ;
77
+ loop {
78
+ if l >= ( ( bits / 2 ) as u8 ) {
79
+ break ;
80
+ }
81
+ // get both sides of the byte boundary
82
+ v[ i] = l - 1 ;
83
+ i += 1 ;
84
+ v[ i] = l;
85
+ i += 1 ;
86
+ l *= 2 ;
87
+ }
88
+
89
+ if bits != 8 {
90
+ // add the lower side of the middle boundary
91
+ v[ i] = ( ( bits / 2 ) - 1 ) as u8 ;
92
+ i += 1 ;
93
+ }
94
+
95
+ // We do not want to jump directly from the Self::BITS/2 boundary to the Self::BITS
96
+ // boundary because of algorithms that split the high part up. We reverse the scaling
97
+ // as we go to Self::BITS.
98
+ let mid = i;
99
+ let mut j = 1 ;
100
+ loop {
101
+ v[ i] = ( bits as u8 ) - ( v[ mid - j] ) - 1 ;
102
+ if j == mid {
103
+ break ;
104
+ }
105
+ i += 1 ;
106
+ j += 1 ;
107
+ }
108
+ v
109
+ }
110
+
43
111
/// Random fuzzing step. When run several times, it results in excellent fuzzing entropy such as:
44
112
/// 11110101010101011110111110011111
45
113
/// 10110101010100001011101011001010
@@ -92,10 +160,9 @@ fn fuzz_step<I: Int>(rng: &mut Xoshiro128StarStar, x: &mut I) {
92
160
macro_rules! edge_cases {
93
161
( $I: ident, $case: ident, $inner: block) => {
94
162
for i0 in 0 ..$I:: FUZZ_NUM {
95
- let mask_lo = ( !$I:: UnsignedInt :: ZERO ) . wrapping_shr( $I:: FUZZ_LENGTHS [ i0] as u32 ) ;
163
+ let mask_lo = ( !$I:: Unsigned :: ZERO ) . wrapping_shr( $I:: FUZZ_LENGTHS [ i0] as u32 ) ;
96
164
for i1 in i0..I :: FUZZ_NUM {
97
- let mask_hi =
98
- ( !$I:: UnsignedInt :: ZERO ) . wrapping_shl( $I:: FUZZ_LENGTHS [ i1 - i0] as u32 ) ;
165
+ let mask_hi = ( !$I:: Unsigned :: ZERO ) . wrapping_shl( $I:: FUZZ_LENGTHS [ i1 - i0] as u32 ) ;
99
166
let $case = I :: from_unsigned( mask_lo & mask_hi) ;
100
167
$inner
101
168
}
@@ -107,7 +174,7 @@ macro_rules! edge_cases {
107
174
/// edge cases, followed by a more random fuzzer that runs `n` times.
108
175
pub fn fuzz < I : Int , F : FnMut ( I ) > ( n : u32 , mut f : F )
109
176
where
110
- <I as MinInt >:: UnsignedInt : Int ,
177
+ <I as MinInt >:: Unsigned : Int ,
111
178
{
112
179
// edge case tester. Calls `f` 210 times for u128.
113
180
// zero gets skipped by the loop
@@ -128,7 +195,7 @@ where
128
195
/// The same as `fuzz`, except `f` has two inputs.
129
196
pub fn fuzz_2 < I : Int , F : Fn ( I , I ) > ( n : u32 , f : F )
130
197
where
131
- <I as MinInt >:: UnsignedInt : Int ,
198
+ <I as MinInt >:: Unsigned : Int ,
132
199
{
133
200
// Check cases where the first and second inputs are zero. Both call `f` 210 times for `u128`.
134
201
edge_cases ! ( I , case, {
0 commit comments