Skip to main content

ctutils/traits/
ct_neg.rs

1use crate::{Choice, CtAssign, CtSelect};
2use core::num::{
3    NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
4    NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
5};
6
7/// Constant-time conditional negation: negates a value when `choice` is [`Choice::TRUE`].
8///
9/// Integer implementations use wrapping negation, including signed and non-zero integers.
10/// Negating a signed integer's minimum value therefore returns that value.
11pub trait CtNeg: Sized {
12    /// Conditionally negate `self`, returning `-self` if `choice` is [`Choice::TRUE`], or `self`
13    /// otherwise.
14    #[must_use]
15    fn ct_neg(&self, choice: Choice) -> Self;
16
17    /// Conditionally negate `self` in-place, replacing it with `-self` if `choice` is
18    /// [`Choice::TRUE`].
19    fn ct_neg_assign(&mut self, choice: Choice) {
20        *self = self.ct_neg(choice);
21    }
22}
23
24// Impl `CtNeg` for a signed integer (`i*`) type which impls `CtSelect`
25macro_rules! impl_signed_ct_neg {
26    ( $($int:ty),+ ) => {
27        $(
28            impl CtNeg for $int {
29                #[inline]
30                fn ct_neg(&self, choice: Choice) -> Self {
31                    self.ct_select(&self.wrapping_neg(), choice)
32                }
33
34                #[inline]
35                fn ct_neg_assign(&mut self, choice: Choice) {
36                    self.ct_assign(&self.wrapping_neg(), choice)
37                }
38            }
39        )+
40    };
41}
42
43// Impl `CtNeg` for an unsigned integer (`u*`) type which impls `CtSelect`
44macro_rules! impl_unsigned_ct_neg {
45    ( $($uint:ty),+ ) => {
46        $(
47            impl CtNeg for $uint {
48                #[inline]
49                fn ct_neg(&self, choice: Choice) -> Self {
50                    self.ct_select(&self.wrapping_neg(), choice)
51                }
52
53                #[inline]
54                fn ct_neg_assign(&mut self, choice: Choice) {
55                    self.ct_assign(&self.wrapping_neg(), choice)
56                }
57            }
58        )+
59    };
60}
61
62impl_signed_ct_neg!(
63    i8,
64    i16,
65    i32,
66    i64,
67    i128,
68    isize,
69    NonZeroI8,
70    NonZeroI16,
71    NonZeroI32,
72    NonZeroI64,
73    NonZeroI128,
74    NonZeroIsize
75);
76impl_unsigned_ct_neg!(u8, u16, u32, u64, u128, usize);
77
78/// Unfortunately `NonZeroU*` doesn't support `wrapping_neg` for some reason (but `NonZeroI*` does),
79/// even though the wrapping negation of any non-zero integer should also be non-zero.
80///
81/// So we need a special case just for `NonZeroU*`, at least for now.
82macro_rules! impl_ct_neg_for_unsigned_nonzero {
83    ( $($nzuint:ident),+ ) => {
84        $(
85            impl CtNeg for $nzuint {
86                #[inline]
87                fn ct_neg(&self, choice: Choice) -> Self {
88                    // TODO(tarcieri): use `NonZero::wrapping_neg` if it becomes available
89                    let n = self.get().ct_select(&self.get().wrapping_neg(), choice);
90                    $nzuint::new(n).expect("should be non-zero")
91                }
92            }
93        )+
94    };
95}
96
97impl_ct_neg_for_unsigned_nonzero!(
98    NonZeroU8,
99    NonZeroU16,
100    NonZeroU32,
101    NonZeroU64,
102    NonZeroU128,
103    NonZeroUsize
104);
105
106#[cfg(test)]
107mod tests {
108    /// Test `CtNeg` impl on `i*`
109    macro_rules! signed_ct_neg_tests {
110         ( $($int:ident),+ ) => {
111             $(
112                mod $int {
113                    use crate::{Choice, CtNeg};
114
115                    #[test]
116                    fn ct_neg() {
117                        let n: $int = 42;
118                        assert_eq!(n, n.ct_neg(Choice::FALSE));
119                        assert_eq!(-n, n.ct_neg(Choice::TRUE));
120                    }
121
122                    #[test]
123                    fn ct_neg_assign() {
124                        let n: $int = 42;
125                        let mut x = n;
126                        x.ct_neg_assign(Choice::FALSE);
127                        assert_eq!(n, x);
128
129                        x.ct_neg_assign(Choice::TRUE);
130                        assert_eq!(-n, x);
131                    }
132
133                    #[test]
134                    fn ct_neg_min() {
135                        let min = <$int>::MIN;
136                        for choice in [Choice::FALSE, Choice::TRUE] {
137                            assert_eq!(min.ct_neg(choice), min);
138                            let mut value = min;
139                            value.ct_neg_assign(choice);
140                            assert_eq!(value, min);
141                        }
142                    }
143                }
144             )+
145        };
146    }
147
148    /// Test `CtNeg` impl on `u*`
149    macro_rules! unsigned_ct_neg_tests {
150         ( $($uint:ident),+ ) => {
151             $(
152                mod $uint {
153                    use crate::{Choice, CtNeg};
154
155                    #[test]
156                    fn ct_neg() {
157                        let n: $uint = 42;
158                        assert_eq!(n, n.ct_neg(Choice::FALSE));
159                        assert_eq!(<$uint>::MAX - n + 1, n.ct_neg(Choice::TRUE));
160                    }
161
162                    #[test]
163                    fn ct_neg_assign() {
164                        let n: $uint = 42;
165                        let mut x = n;
166                        x.ct_neg_assign(Choice::FALSE);
167                        assert_eq!(n, x);
168
169                        x.ct_neg_assign(Choice::TRUE);
170                        assert_eq!(<$uint>::MAX - n + 1, x);
171                    }
172                }
173             )+
174        };
175    }
176
177    signed_ct_neg_tests!(i8, i16, i32, i64, i128, isize);
178    unsigned_ct_neg_tests!(u8, u16, u32, u64, u128, usize);
179
180    macro_rules! nonzero_signed_ct_neg_tests {
181        ($(($test:ident, $ty:ty, $int:ty)),+ $(,)?) => {
182            $(
183                #[test]
184                fn $test() {
185                    use crate::{Choice, CtNeg};
186                    let min = <$ty>::new(<$int>::MIN).expect("MIN is non-zero");
187                    for choice in [Choice::FALSE, Choice::TRUE] {
188                        assert_eq!(min.ct_neg(choice), min);
189                        let mut value = min;
190                        value.ct_neg_assign(choice);
191                        assert_eq!(value, min);
192                    }
193                }
194            )+
195        };
196    }
197
198    nonzero_signed_ct_neg_tests!(
199        (nonzero_i8_min, core::num::NonZeroI8, i8),
200        (nonzero_i16_min, core::num::NonZeroI16, i16),
201        (nonzero_i32_min, core::num::NonZeroI32, i32),
202        (nonzero_i64_min, core::num::NonZeroI64, i64),
203        (nonzero_i128_min, core::num::NonZeroI128, i128),
204        (nonzero_isize_min, core::num::NonZeroIsize, isize),
205    );
206}