1use crate::Choice;
2use core::{
3 cmp,
4 num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize},
5};
6
7pub trait CtLt {
9 #[must_use]
11 fn ct_lt(&self, other: &Self) -> Choice;
12}
13
14macro_rules! impl_unsigned_ct_lt {
15 ( $(($uint:ty, $from_lt:ident)),+ ) => {
16 $(
17 impl CtLt for $uint {
18 #[inline]
19 fn ct_lt(&self, other: &Self) -> Choice {
20 #[cfg(all(target_arch = "arm", not(target_has_atomic = "ptr")))]
24 {
25 Choice::$from_lt(*self, *other)
26 }
27 #[cfg(not(all(target_arch = "arm", not(target_has_atomic = "ptr"))))]
29 {
30 let (_, overflow) = self.overflowing_sub(*other);
31 Choice(overflow.into())
32 }
33 }
34 }
35 )+
36 };
37}
38
39impl_unsigned_ct_lt!((u8, from_u8_lt), (u16, from_u16_lt), (u32, from_u32_lt));
40
41#[cfg(not(target_arch = "riscv32"))]
42impl_unsigned_ct_lt!((u64, from_u64_lt));
43
44#[cfg(target_arch = "riscv32")]
45impl CtLt for u64 {
46 #[inline]
47 fn ct_lt(&self, other: &Self) -> Choice {
48 Choice::from_u64_lt(*self, *other)
50 }
51}
52
53impl CtLt for usize {
54 #[inline]
55 #[allow(clippy::cast_possible_truncation, reason = "matches pointer width")]
56 fn ct_lt(&self, other: &Self) -> Choice {
57 #[cfg(target_pointer_width = "16")]
58 let (a, b) = (*self as u16, *other as u16);
59 #[cfg(target_pointer_width = "32")]
60 let (a, b) = (*self as u32, *other as u32);
61 #[cfg(target_pointer_width = "64")]
62 let (a, b) = (*self as u64, *other as u64);
63 a.ct_lt(&b)
64 }
65}
66
67impl CtLt for u128 {
68 #[inline]
69 fn ct_lt(&self, other: &Self) -> Choice {
70 Choice::from_u128_lt(*self, *other)
71 }
72}
73
74macro_rules! impl_ct_lt_for_nonzero_integer {
76 ( $($ty:ty),+ ) => {
77 $(
78 impl CtLt for $ty {
79 #[inline]
80 fn ct_lt(&self, other: &Self) -> Choice {
81 self.get().ct_lt(&other.get())
82 }
83 }
84 )+
85 };
86}
87
88impl_ct_lt_for_nonzero_integer!(
89 NonZeroU8,
90 NonZeroU16,
91 NonZeroU32,
92 NonZeroU64,
93 NonZeroU128,
94 NonZeroUsize
95);
96
97impl CtLt for cmp::Ordering {
98 #[inline]
99 #[allow(clippy::arithmetic_side_effects, clippy::cast_sign_loss)]
100 fn ct_lt(&self, other: &Self) -> Choice {
101 let a = (*self as i8) + 1;
102 let b = (*other as i8) + 1;
103
104 a.cast_unsigned().ct_lt(&b.cast_unsigned())
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::CtLt;
112 use core::cmp::Ordering;
113
114 #[test]
115 fn ct_lt() {
116 let a = 42u64;
117 let b = 43u64;
118 assert!(!a.ct_lt(&a).to_bool());
119 assert!(a.ct_lt(&b).to_bool());
120 assert!(!b.ct_lt(&a).to_bool());
121 }
122
123 macro_rules! ct_lt_tests {
125 ( $($int:ident),+ ) => {
126 $(
127 mod $int {
128 use super::CtLt;
129
130 #[test]
131 fn ct_gt() {
132 let a = <$int>::MIN;
133 let b = <$int>::MAX;
134 assert!(!a.ct_lt(&a).to_bool());
135 assert!(a.ct_lt(&b).to_bool());
136 assert!(!b.ct_lt(&a).to_bool());
137 }
138
139 }
140 )+
141 };
142 }
143
144 ct_lt_tests!(u8, u16, u32, u64, u128, usize);
145
146 #[test]
147 fn ordering() {
148 assert!(!Ordering::Equal.ct_lt(&Ordering::Equal).to_bool());
149 assert!(Ordering::Less.ct_lt(&Ordering::Greater).to_bool());
150 assert!(!Ordering::Greater.ct_lt(&Ordering::Less).to_bool());
151 }
152}