Skip to main content

ctutils/traits/
ct_lt.rs

1use crate::Choice;
2use core::{
3    cmp,
4    num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize},
5};
6
7/// Constant time less than.
8pub trait CtLt {
9    /// Compute whether `self < other` in constant time.
10    #[must_use]
11    fn ct_lt(&self, other: &Self) -> Choice;
12}
13
14// Impl `CtLt` using overflowing subtraction
15macro_rules! impl_unsigned_ct_lt {
16    ( $($uint:ty),+ ) => {
17        $(
18            impl CtLt for $uint {
19                #[inline]
20                fn ct_lt(&self, other: &Self) -> Choice {
21                    let (_, overflow) = self.overflowing_sub(*other);
22                    Choice(overflow.into())
23                }
24            }
25        )+
26    };
27}
28
29impl_unsigned_ct_lt!(u8, u16, u32, u64, usize);
30
31impl CtLt for u128 {
32    #[inline]
33    fn ct_lt(&self, other: &Self) -> Choice {
34        Choice::from_u128_lt(*self, *other)
35    }
36}
37
38/// Impl `CtLt` for `NonZero<T>` by calling `NonZero::get`.
39macro_rules! impl_ct_lt_for_nonzero_integer {
40    ( $($ty:ty),+ ) => {
41        $(
42            impl CtLt for $ty {
43                #[inline]
44                fn ct_lt(&self, other: &Self) -> Choice {
45                    self.get().ct_lt(&other.get())
46                }
47            }
48        )+
49    };
50}
51
52impl_ct_lt_for_nonzero_integer!(
53    NonZeroU8,
54    NonZeroU16,
55    NonZeroU32,
56    NonZeroU64,
57    NonZeroU128,
58    NonZeroUsize
59);
60
61impl CtLt for cmp::Ordering {
62    #[inline]
63    #[allow(clippy::arithmetic_side_effects, clippy::cast_sign_loss)]
64    fn ct_lt(&self, other: &Self) -> Choice {
65        let a = (*self as i8) + 1;
66        let b = (*other as i8) + 1;
67
68        // No impl of `CtLt` for `i8`, so use `u8`
69        a.cast_unsigned().ct_lt(&b.cast_unsigned())
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::CtLt;
76    use core::cmp::Ordering;
77
78    #[test]
79    fn ct_lt() {
80        let a = 42u64;
81        let b = 43u64;
82        assert!(!a.ct_lt(&a).to_bool());
83        assert!(a.ct_lt(&b).to_bool());
84        assert!(!b.ct_lt(&a).to_bool());
85    }
86
87    /// Test `CtLt`
88    macro_rules! ct_lt_tests {
89         ( $($int:ident),+ ) => {
90             $(
91                mod $int {
92                    use super::CtLt;
93
94                    #[test]
95                    fn ct_gt() {
96                        let a = <$int>::MIN;
97                        let b = <$int>::MAX;
98                        assert!(!a.ct_lt(&a).to_bool());
99                        assert!(a.ct_lt(&b).to_bool());
100                        assert!(!b.ct_lt(&a).to_bool());
101                    }
102
103                }
104             )+
105        };
106    }
107
108    ct_lt_tests!(u8, u16, u32, u64, u128, usize);
109
110    #[test]
111    fn ordering() {
112        assert!(!Ordering::Equal.ct_lt(&Ordering::Equal).to_bool());
113        assert!(Ordering::Less.ct_lt(&Ordering::Greater).to_bool());
114        assert!(!Ordering::Greater.ct_lt(&Ordering::Less).to_bool());
115    }
116}