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