test_helpers/
biteq.rs

1//! Compare numeric types by exact bit value.
2
3pub trait BitEq {
4    fn biteq(&self, other: &Self) -> bool;
5    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result;
6}
7
8impl BitEq for bool {
9    fn biteq(&self, other: &Self) -> bool {
10        self == other
11    }
12
13    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
14        write!(f, "{:?}", self)
15    }
16}
17
18macro_rules! impl_integer_biteq {
19    { $($type:ty),* } => {
20        $(
21        impl BitEq for $type {
22            fn biteq(&self, other: &Self) -> bool {
23                self == other
24            }
25
26            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
27                write!(f, "{:?} ({:x})", self, self)
28            }
29        }
30        )*
31    };
32}
33
34impl_integer_biteq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize }
35
36macro_rules! impl_float_biteq {
37    { $($type:ty),* } => {
38        $(
39        impl BitEq for $type {
40            fn biteq(&self, other: &Self) -> bool {
41                if self.is_nan() && other.is_nan() {
42                    true // exact nan bits don't matter
43                } else {
44                    self.to_bits() == other.to_bits()
45                }
46            }
47
48            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
49                write!(f, "{:?} ({:x})", self, self.to_bits())
50            }
51        }
52        )*
53    };
54}
55
56impl_float_biteq! { f32, f64 }
57
58impl<T> BitEq for *const T {
59    fn biteq(&self, other: &Self) -> bool {
60        self == other
61    }
62
63    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
64        write!(f, "{:?}", self)
65    }
66}
67
68impl<T> BitEq for *mut T {
69    fn biteq(&self, other: &Self) -> bool {
70        self == other
71    }
72
73    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
74        write!(f, "{:?}", self)
75    }
76}
77
78impl<T: BitEq, const N: usize> BitEq for [T; N] {
79    fn biteq(&self, other: &Self) -> bool {
80        self.iter()
81            .zip(other.iter())
82            .fold(true, |value, (left, right)| value && left.biteq(right))
83    }
84
85    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
86        #[repr(transparent)]
87        struct Wrapper<'a, T: BitEq>(&'a T);
88
89        impl<T: BitEq> core::fmt::Debug for Wrapper<'_, T> {
90            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
91                self.0.fmt(f)
92            }
93        }
94
95        f.debug_list()
96            .entries(self.iter().map(|x| Wrapper(x)))
97            .finish()
98    }
99}
100
101#[doc(hidden)]
102pub struct BitEqWrapper<'a, T>(pub &'a T);
103
104impl<T: BitEq> PartialEq for BitEqWrapper<'_, T> {
105    fn eq(&self, other: &Self) -> bool {
106        self.0.biteq(other.0)
107    }
108}
109
110impl<T: BitEq> core::fmt::Debug for BitEqWrapper<'_, T> {
111    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
112        self.0.fmt(f)
113    }
114}
115
116#[doc(hidden)]
117pub struct BitEqEitherWrapper<'a, T>(pub &'a T, pub &'a T);
118
119impl<T: BitEq> PartialEq<BitEqEitherWrapper<'_, T>> for BitEqWrapper<'_, T> {
120    fn eq(&self, other: &BitEqEitherWrapper<'_, T>) -> bool {
121        self.0.biteq(other.0) || self.0.biteq(other.1)
122    }
123}
124
125impl<T: BitEq> core::fmt::Debug for BitEqEitherWrapper<'_, T> {
126    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
127        if self.0.biteq(self.1) {
128            self.0.fmt(f)
129        } else {
130            self.0.fmt(f)?;
131            write!(f, " or ")?;
132            self.1.fmt(f)
133        }
134    }
135}
136
137#[macro_export]
138macro_rules! prop_assert_biteq {
139    { $a:expr, $b:expr $(,)? } => {
140        {
141            use $crate::biteq::BitEqWrapper;
142            let a = $a;
143            let b = $b;
144            proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqWrapper(&b));
145        }
146    };
147    { $a:expr, $b:expr, $c:expr $(,)? } => {
148        {
149            use $crate::biteq::{BitEqWrapper, BitEqEitherWrapper};
150            let a = $a;
151            let b = $b;
152            let c = $c;
153            proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqEitherWrapper(&b, &c));
154        }
155    };
156}