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, u128, usize);
30
31/// Impl `CtLt` for `NonZero<T>` by calling `NonZero::get`.
32macro_rules! impl_ct_lt_for_nonzero_integer {
33    ( $($ty:ty),+ ) => {
34        $(
35            impl CtLt for $ty {
36                #[inline]
37                fn ct_lt(&self, other: &Self) -> Choice {
38                    self.get().ct_lt(&other.get())
39                }
40            }
41        )+
42    };
43}
44
45impl_ct_lt_for_nonzero_integer!(
46    NonZeroU8,
47    NonZeroU16,
48    NonZeroU32,
49    NonZeroU64,
50    NonZeroU128,
51    NonZeroUsize
52);
53
54impl CtLt for cmp::Ordering {
55    #[inline]
56    #[allow(clippy::arithmetic_side_effects, clippy::cast_sign_loss)]
57    fn ct_lt(&self, other: &Self) -> Choice {
58        // No impl of `CtLt` for `i8`, so use `u8`
59        // TODO(tarcieri): use `cast_signed` when MSRV is 1.87
60        let a = (*self as i8) + 1;
61        let b = (*other as i8) + 1;
62
63        // TODO(tarcieri): use `cast_unsigned` when MSRV is 1.87
64        (a as u8).ct_lt(&(b as u8))
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::CtLt;
71    use core::cmp::Ordering;
72
73    #[test]
74    fn ct_lt() {
75        let a = 42u64;
76        let b = 43u64;
77        assert!(!a.ct_lt(&a).to_bool());
78        assert!(a.ct_lt(&b).to_bool());
79        assert!(!b.ct_lt(&a).to_bool());
80    }
81
82    /// Test `CtLt`
83    macro_rules! ct_lt_tests {
84         ( $($int:ident),+ ) => {
85             $(
86                mod $int {
87                    use super::CtLt;
88
89                    #[test]
90                    fn ct_gt() {
91                        let a = <$int>::MIN;
92                        let b = <$int>::MAX;
93                        assert!(!a.ct_lt(&a).to_bool());
94                        assert!(a.ct_lt(&b).to_bool());
95                        assert!(!b.ct_lt(&a).to_bool());
96                    }
97
98                }
99             )+
100        };
101    }
102
103    ct_lt_tests!(u8, u16, u32, u64, u128, usize);
104
105    #[test]
106    fn ordering() {
107        assert!(!Ordering::Equal.ct_lt(&Ordering::Equal).to_bool());
108        assert!(Ordering::Less.ct_lt(&Ordering::Greater).to_bool());
109        assert!(!Ordering::Greater.ct_lt(&Ordering::Less).to_bool());
110    }
111}