Browse Source

Introduce the `DInt` and `HInt` traits

and add various methods that will be used for improved fuzzing
Aaron Kutch 4 years ago
parent
commit
b1a7a00e48
1 changed files with 202 additions and 1 deletions
  1. 202 1
      src/int/mod.rs

+ 202 - 1
src/int/mod.rs

@@ -12,13 +12,15 @@ pub mod udiv;
 pub use self::leading_zeros::__clzsi2;
 
 /// Trait for some basic operations on integers
-pub(crate) trait Int:
+#[doc(hidden)]
+pub trait Int:
     Copy
     + PartialEq
     + PartialOrd
     + ops::AddAssign
     + ops::BitAndAssign
     + ops::BitOrAssign
+    + ops::BitXorAssign
     + ops::ShlAssign<i32>
     + ops::ShrAssign<u32>
     + ops::Add<Output = Self>
@@ -41,6 +43,14 @@ pub(crate) trait Int:
 
     const ZERO: Self;
     const ONE: Self;
+    const MIN: Self;
+
+    /// LUT used for maximizing the space covered and minimizing the computational cost of fuzzing
+    /// in `testcrate`. For example, Self = u128 produces [0,1,2,7,8,15,16,31,32,63,64,95,96,111,
+    /// 112,119,120,125,126,127].
+    const FUZZ_LENGTHS: [u8; 20];
+    /// The number of entries of `FUZZ_LENGTHS` actually used. The maximum is 20 for u128.
+    const FUZZ_NUM: usize;
 
     /// Extracts the sign from self and returns a tuple.
     ///
@@ -59,17 +69,25 @@ pub(crate) trait Int:
 
     fn from_bool(b: bool) -> Self;
 
+    /// Prevents the need for excessive conversions between signed and unsigned
+    fn logical_shr(self, other: u32) -> Self;
+
     // copied from primitive integers, but put in a trait
+    fn is_zero(self) -> bool;
     fn max_value() -> Self;
     fn min_value() -> Self;
+    fn wrapping_neg(self) -> Self;
     fn wrapping_add(self, other: Self) -> Self;
     fn wrapping_mul(self, other: Self) -> Self;
     fn wrapping_sub(self, other: Self) -> Self;
     fn wrapping_shl(self, other: u32) -> Self;
+    fn wrapping_shr(self, other: u32) -> Self;
+    fn rotate_left(self, other: u32) -> Self;
     fn overflowing_add(self, other: Self) -> (Self, bool);
     fn aborting_div(self, other: Self) -> Self;
     fn aborting_rem(self, other: Self) -> Self;
     fn leading_zeros(self) -> u32;
+    fn count_ones(self) -> u32;
 }
 
 fn unwrap<T>(t: Option<T>) -> T {
@@ -85,11 +103,78 @@ macro_rules! int_impl_common {
 
         const ZERO: Self = 0;
         const ONE: Self = 1;
+        const MIN: Self = <Self>::MIN;
+
+        const FUZZ_LENGTHS: [u8; 20] = {
+            let bits = <Self as Int>::BITS;
+            let mut v = [0u8; 20];
+            v[0] = 0;
+            v[1] = 1;
+            v[2] = 2; // important for parity and the iX::MIN case when reversed
+            let mut i = 3;
+            // No need for any more until the byte boundary, because there should be no algorithms
+            // that are sensitive to anything not next to byte boundaries after 2. We also scale
+            // in powers of two, which is important to prevent u128 corner tests from getting too
+            // big.
+            let mut l = 8;
+            loop {
+                if l >= ((bits / 2) as u8) {
+                    break;
+                }
+                // get both sides of the byte boundary
+                v[i] = l - 1;
+                i += 1;
+                v[i] = l;
+                i += 1;
+                l *= 2;
+            }
+
+            if bits != 8 {
+                // add the lower side of the middle boundary
+                v[i] = ((bits / 2) - 1) as u8;
+                i += 1;
+            }
+
+            // We do not want to jump directly from the Self::BITS/2 boundary to the Self::BITS
+            // boundary because of algorithms that split the high part up. We reverse the scaling
+            // as we go to Self::BITS.
+            let mid = i;
+            let mut j = 1;
+            loop {
+                v[i] = (bits as u8) - (v[mid - j]) - 1;
+                if j == mid {
+                    break;
+                }
+                i += 1;
+                j += 1;
+            }
+            v
+        };
+
+        const FUZZ_NUM: usize = {
+            let log2 = (<Self as Int>::BITS - 1).count_ones() as usize;
+            if log2 == 3 {
+                // case for u8
+                6
+            } else {
+                // 3 entries on each extreme, 2 in the middle, and 4 for each scale of intermediate
+                // boundaries.
+                8 + (4 * (log2 - 4))
+            }
+        };
 
         fn from_bool(b: bool) -> Self {
             b as $ty
         }
 
+        fn logical_shr(self, other: u32) -> Self {
+            Self::from_unsigned(self.unsigned().wrapping_shr(other))
+        }
+
+        fn is_zero(self) -> bool {
+            self == Self::ZERO
+        }
+
         fn max_value() -> Self {
             <Self>::max_value()
         }
@@ -98,6 +183,10 @@ macro_rules! int_impl_common {
             <Self>::min_value()
         }
 
+        fn wrapping_neg(self) -> Self {
+            <Self>::wrapping_neg(self)
+        }
+
         fn wrapping_add(self, other: Self) -> Self {
             <Self>::wrapping_add(self, other)
         }
@@ -114,6 +203,14 @@ macro_rules! int_impl_common {
             <Self>::wrapping_shl(self, other)
         }
 
+        fn wrapping_shr(self, other: u32) -> Self {
+            <Self>::wrapping_shr(self, other)
+        }
+
+        fn rotate_left(self, other: u32) -> Self {
+            <Self>::rotate_left(self, other)
+        }
+
         fn overflowing_add(self, other: Self) -> (Self, bool) {
             <Self>::overflowing_add(self, other)
         }
@@ -129,6 +226,10 @@ macro_rules! int_impl_common {
         fn leading_zeros(self) -> u32 {
             <Self>::leading_zeros(self)
         }
+
+        fn count_ones(self) -> u32 {
+            <Self>::count_ones(self)
+        }
     };
 }
 
@@ -178,11 +279,111 @@ macro_rules! int_impl {
     };
 }
 
+int_impl!(isize, usize, usize::MAX.count_ones());
+int_impl!(i8, u8, 8);
 int_impl!(i16, u16, 16);
 int_impl!(i32, u32, 32);
 int_impl!(i64, u64, 64);
 int_impl!(i128, u128, 128);
 
+/// Trait for integers twice the bit width of another integer. This is implemented for all
+/// primitives except for `u8`, because there is not a smaller primitive.
+#[doc(hidden)]
+pub trait DInt: Int {
+    /// Integer that is half the bit width of the integer this trait is implemented for
+    type H: HInt<D = Self> + Int;
+
+    /// Returns the low half of `self`
+    fn lo(self) -> Self::H;
+    /// Returns the high half of `self`
+    fn hi(self) -> Self::H;
+    /// Returns the low and high halves of `self` as a tuple
+    fn lo_hi(self) -> (Self::H, Self::H);
+    /// Constructs an integer using lower and higher half parts
+    fn from_lo_hi(lo: Self::H, hi: Self::H) -> Self;
+}
+
+/// Trait for integers half the bit width of another integer. This is implemented for all
+/// primitives except for `u128`, because it there is not a larger primitive.
+#[doc(hidden)]
+pub trait HInt: Int {
+    /// Integer that is double the bit width of the integer this trait is implemented for
+    type D: DInt<H = Self> + Int;
+
+    /// Widens (using default extension) the integer to have double bit width
+    fn widen(self) -> Self::D;
+    /// Widens (zero extension only) the integer to have double bit width. This is needed to get
+    /// around problems with associated type bounds (such as `Int<Othersign: DInt>`) being unstable
+    fn zero_widen(self) -> Self::D;
+    /// Widens the integer to have double bit width and shifts the integer into the higher bits
+    fn widen_hi(self) -> Self::D;
+    /// Widening multiplication with zero widening. This cannot overflow.
+    fn zero_widen_mul(self, rhs: Self) -> Self::D;
+    /// Widening multiplication. This cannot overflow.
+    fn widen_mul(self, rhs: Self) -> Self::D;
+}
+
+macro_rules! impl_d_int {
+    ($($X:ident $D:ident),*) => {
+        $(
+            impl DInt for $D {
+                type H = $X;
+
+                fn lo(self) -> Self::H {
+                    self as $X
+                }
+                fn hi(self) -> Self::H {
+                    (self >> <$X as Int>::BITS) as $X
+                }
+                fn lo_hi(self) -> (Self::H, Self::H) {
+                    (self.lo(), self.hi())
+                }
+                fn from_lo_hi(lo: Self::H, hi: Self::H) -> Self {
+                    lo.zero_widen() | hi.widen_hi()
+                }
+            }
+        )*
+    };
+}
+
+macro_rules! impl_h_int {
+    ($($H:ident $uH:ident $X:ident),*) => {
+        $(
+            impl HInt for $H {
+                type D = $X;
+
+                fn widen(self) -> Self::D {
+                    self as $X
+                }
+                fn zero_widen(self) -> Self::D {
+                    (self as $uH) as $X
+                }
+                fn widen_hi(self) -> Self::D {
+                    (self as $X) << <$H as Int>::BITS
+                }
+                fn zero_widen_mul(self, rhs: Self) -> Self::D {
+                    self.zero_widen().wrapping_mul(rhs.zero_widen())
+                }
+                fn widen_mul(self, rhs: Self) -> Self::D {
+                    self.widen().wrapping_mul(rhs.widen())
+                }
+            }
+        )*
+    };
+}
+
+impl_d_int!(u8 u16, u16 u32, u32 u64, u64 u128, i8 i16, i16 i32, i32 i64, i64 i128);
+impl_h_int!(
+    u8 u8 u16,
+    u16 u16 u32,
+    u32 u32 u64,
+    u64 u64 u128,
+    i8 u8 i16,
+    i16 u16 i32,
+    i32 u32 i64,
+    i64 u64 i128
+);
+
 /// Trait to convert an integer to/from smaller parts
 pub(crate) trait LargeInt: Int {
     type LowHalf: Int;