@@ -40,6 +40,75 @@ pub const N: u32 = if cfg!(target_arch = "x86_64") && !cfg!(debug_assertions) {
4040 10_000
4141} ;
4242
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+
43112/// Random fuzzing step. When run several times, it results in excellent fuzzing entropy such as:
44113/// 11110101010101011110111110011111
45114/// 10110101010100001011101011001010
@@ -92,10 +161,9 @@ fn fuzz_step<I: Int>(rng: &mut Xoshiro128StarStar, x: &mut I) {
92161macro_rules! edge_cases {
93162 ( $I: ident, $case: ident, $inner: block) => {
94163 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 ) ;
96165 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 ) ;
99167 let $case = I :: from_unsigned( mask_lo & mask_hi) ;
100168 $inner
101169 }
@@ -107,7 +175,7 @@ macro_rules! edge_cases {
107175/// edge cases, followed by a more random fuzzer that runs `n` times.
108176pub fn fuzz < I : Int , F : FnMut ( I ) > ( n : u32 , mut f : F )
109177where
110- <I as MinInt >:: UnsignedInt : Int ,
178+ <I as MinInt >:: Unsigned : Int ,
111179{
112180 // edge case tester. Calls `f` 210 times for u128.
113181 // zero gets skipped by the loop
@@ -128,7 +196,7 @@ where
128196/// The same as `fuzz`, except `f` has two inputs.
129197pub fn fuzz_2 < I : Int , F : Fn ( I , I ) > ( n : u32 , f : F )
130198where
131- <I as MinInt >:: UnsignedInt : Int ,
199+ <I as MinInt >:: Unsigned : Int ,
132200{
133201 // Check cases where the first and second inputs are zero. Both call `f` 210 times for `u128`.
134202 edge_cases ! ( I , case, {
0 commit comments