@@ -40,6 +40,74 @@ pub const N: u32 = if cfg!(target_arch = "x86_64") && !cfg!(debug_assertions) {
4040 10_000
4141} ;
4242
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+
43111/// Random fuzzing step. When run several times, it results in excellent fuzzing entropy such as:
44112/// 11110101010101011110111110011111
45113/// 10110101010100001011101011001010
@@ -92,10 +160,9 @@ fn fuzz_step<I: Int>(rng: &mut Xoshiro128StarStar, x: &mut I) {
92160macro_rules! edge_cases {
93161 ( $I: ident, $case: ident, $inner: block) => {
94162 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 ) ;
96164 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 ) ;
99166 let $case = I :: from_unsigned( mask_lo & mask_hi) ;
100167 $inner
101168 }
@@ -107,7 +174,7 @@ macro_rules! edge_cases {
107174/// edge cases, followed by a more random fuzzer that runs `n` times.
108175pub fn fuzz < I : Int , F : FnMut ( I ) > ( n : u32 , mut f : F )
109176where
110- <I as MinInt >:: UnsignedInt : Int ,
177+ <I as MinInt >:: Unsigned : Int ,
111178{
112179 // edge case tester. Calls `f` 210 times for u128.
113180 // zero gets skipped by the loop
@@ -128,7 +195,7 @@ where
128195/// The same as `fuzz`, except `f` has two inputs.
129196pub fn fuzz_2 < I : Int , F : Fn ( I , I ) > ( n : u32 , f : F )
130197where
131- <I as MinInt >:: UnsignedInt : Int ,
198+ <I as MinInt >:: Unsigned : Int ,
132199{
133200 // Check cases where the first and second inputs are zero. Both call `f` 210 times for `u128`.
134201 edge_cases ! ( I , case, {
0 commit comments