Skip to main content

ctutils/traits/
ct_gt.rs

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