Skip to main content

const_num_traits/ops/
checked.rs

1//! Checked arithmetic: `None` on overflow.
2//!
3//! **CT tier B (caller-leaky)** for the branchless checked atoms
4//! (add/sub/mul/neg/shifts/abs): the `Option` return invites branching on
5//! secret-derived data at the call site. Masked counterparts live in `ops::ct`
6//! (cargo feature `ct`). `CheckedDiv`/`CheckedRem` are CT-hostile (data-dependent
7//! division) and `CheckedPow` is exponent-dependent — Tier C for secret inputs.
8
9use core::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub};
10
11c0nst::c0nst! {
12/// Performs addition, returning `None` if overflow occurred.
13pub c0nst trait CheckedAdd: Sized + [c0nst] Add<Self> {
14    /// Adds two numbers, checking for overflow. If overflow happens, `None` is
15    /// returned.
16    fn checked_add(self, v: Self) -> Option<<Self as Add<Self>>::Output>;
17}
18}
19
20macro_rules! checked_impl {
21    ($trait_name:ident, $method:ident, $t:ty) => {
22        c0nst::c0nst! {
23        c0nst impl $trait_name for $t {
24            #[inline]
25            fn $method(self, v: $t) -> Option<$t> {
26                <$t>::$method(self, v)
27            }
28        }
29        }
30    };
31}
32
33checked_impl!(CheckedAdd, checked_add, u8);
34checked_impl!(CheckedAdd, checked_add, u16);
35checked_impl!(CheckedAdd, checked_add, u32);
36checked_impl!(CheckedAdd, checked_add, u64);
37checked_impl!(CheckedAdd, checked_add, usize);
38checked_impl!(CheckedAdd, checked_add, u128);
39
40checked_impl!(CheckedAdd, checked_add, i8);
41checked_impl!(CheckedAdd, checked_add, i16);
42checked_impl!(CheckedAdd, checked_add, i32);
43checked_impl!(CheckedAdd, checked_add, i64);
44checked_impl!(CheckedAdd, checked_add, isize);
45checked_impl!(CheckedAdd, checked_add, i128);
46
47c0nst::c0nst! {
48/// Performs subtraction, returning `None` if overflow occurred.
49pub c0nst trait CheckedSub: Sized + [c0nst] Sub<Self> {
50    /// Subtracts two numbers, checking for overflow. If overflow happens,
51    /// `None` is returned.
52    fn checked_sub(self, v: Self) -> Option<<Self as Sub<Self>>::Output>;
53}
54}
55
56checked_impl!(CheckedSub, checked_sub, u8);
57checked_impl!(CheckedSub, checked_sub, u16);
58checked_impl!(CheckedSub, checked_sub, u32);
59checked_impl!(CheckedSub, checked_sub, u64);
60checked_impl!(CheckedSub, checked_sub, usize);
61checked_impl!(CheckedSub, checked_sub, u128);
62
63checked_impl!(CheckedSub, checked_sub, i8);
64checked_impl!(CheckedSub, checked_sub, i16);
65checked_impl!(CheckedSub, checked_sub, i32);
66checked_impl!(CheckedSub, checked_sub, i64);
67checked_impl!(CheckedSub, checked_sub, isize);
68checked_impl!(CheckedSub, checked_sub, i128);
69
70c0nst::c0nst! {
71/// Performs multiplication, returning `None` if overflow occurred.
72pub c0nst trait CheckedMul: Sized + [c0nst] Mul<Self> {
73    /// Multiplies two numbers, checking for overflow. If overflow happens,
74    /// `None` is returned.
75    fn checked_mul(self, v: Self) -> Option<<Self as Mul<Self>>::Output>;
76}
77}
78
79checked_impl!(CheckedMul, checked_mul, u8);
80checked_impl!(CheckedMul, checked_mul, u16);
81checked_impl!(CheckedMul, checked_mul, u32);
82checked_impl!(CheckedMul, checked_mul, u64);
83checked_impl!(CheckedMul, checked_mul, usize);
84checked_impl!(CheckedMul, checked_mul, u128);
85
86checked_impl!(CheckedMul, checked_mul, i8);
87checked_impl!(CheckedMul, checked_mul, i16);
88checked_impl!(CheckedMul, checked_mul, i32);
89checked_impl!(CheckedMul, checked_mul, i64);
90checked_impl!(CheckedMul, checked_mul, isize);
91checked_impl!(CheckedMul, checked_mul, i128);
92
93c0nst::c0nst! {
94/// Performs division, returning `None` on division by zero or if overflow
95/// occurred.
96pub c0nst trait CheckedDiv: Sized + [c0nst] Div<Self> {
97    /// Divides two numbers, checking for overflow and division by
98    /// zero. If any of that happens, `None` is returned.
99    fn checked_div(self, v: Self) -> Option<<Self as Div<Self>>::Output>;
100}
101}
102
103checked_impl!(CheckedDiv, checked_div, u8);
104checked_impl!(CheckedDiv, checked_div, u16);
105checked_impl!(CheckedDiv, checked_div, u32);
106checked_impl!(CheckedDiv, checked_div, u64);
107checked_impl!(CheckedDiv, checked_div, usize);
108checked_impl!(CheckedDiv, checked_div, u128);
109
110checked_impl!(CheckedDiv, checked_div, i8);
111checked_impl!(CheckedDiv, checked_div, i16);
112checked_impl!(CheckedDiv, checked_div, i32);
113checked_impl!(CheckedDiv, checked_div, i64);
114checked_impl!(CheckedDiv, checked_div, isize);
115checked_impl!(CheckedDiv, checked_div, i128);
116
117c0nst::c0nst! {
118/// Performs integral remainder, returning `None` on division by zero or if
119/// overflow occurred.
120pub c0nst trait CheckedRem: Sized + [c0nst] Rem<Self> {
121    /// Finds the remainder of dividing two numbers, checking for overflow and
122    /// division by zero. If any of that happens, `None` is returned.
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// use const_num_traits::CheckedRem;
128    /// use std::i32::MIN;
129    ///
130    /// assert_eq!(CheckedRem::checked_rem(10, 7), Some(3));
131    /// assert_eq!(CheckedRem::checked_rem(10, -7), Some(3));
132    /// assert_eq!(CheckedRem::checked_rem(-10, 7), Some(-3));
133    /// assert_eq!(CheckedRem::checked_rem(-10, -7), Some(-3));
134    ///
135    /// assert_eq!(CheckedRem::checked_rem(10, 0), None);
136    ///
137    /// assert_eq!(CheckedRem::checked_rem(MIN, 1), Some(0));
138    /// assert_eq!(CheckedRem::checked_rem(MIN, -1), None);
139    /// ```
140    fn checked_rem(self, v: Self) -> Option<<Self as Rem<Self>>::Output>;
141}
142}
143
144checked_impl!(CheckedRem, checked_rem, u8);
145checked_impl!(CheckedRem, checked_rem, u16);
146checked_impl!(CheckedRem, checked_rem, u32);
147checked_impl!(CheckedRem, checked_rem, u64);
148checked_impl!(CheckedRem, checked_rem, usize);
149checked_impl!(CheckedRem, checked_rem, u128);
150
151checked_impl!(CheckedRem, checked_rem, i8);
152checked_impl!(CheckedRem, checked_rem, i16);
153checked_impl!(CheckedRem, checked_rem, i32);
154checked_impl!(CheckedRem, checked_rem, i64);
155checked_impl!(CheckedRem, checked_rem, isize);
156checked_impl!(CheckedRem, checked_rem, i128);
157
158macro_rules! checked_impl_unary {
159    ($trait_name:ident, $method:ident, $t:ty) => {
160        c0nst::c0nst! {
161        c0nst impl $trait_name for $t {
162            #[inline]
163            fn $method(self) -> Option<$t> {
164                <$t>::$method(self)
165            }
166        }
167        }
168    };
169}
170
171c0nst::c0nst! {
172/// Performs negation, returning `None` if the result can't be represented.
173pub c0nst trait CheckedNeg: Sized {
174    /// The negation result type (`Self` for the primitive impls; a distinct
175    /// owned type lets non-`Copy` types implement this for references).
176    type Output;
177
178    /// Negates a number, returning `None` for results that can't be represented, like signed `MIN`
179    /// values that can't be positive, or non-zero unsigned values that can't be negative.
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use const_num_traits::CheckedNeg;
185    /// use std::i32::MIN;
186    ///
187    /// assert_eq!(CheckedNeg::checked_neg(1_i32), Some(-1));
188    /// assert_eq!(CheckedNeg::checked_neg(-1_i32), Some(1));
189    /// assert_eq!(CheckedNeg::checked_neg(MIN), None);
190    ///
191    /// assert_eq!(CheckedNeg::checked_neg(0_u32), Some(0));
192    /// assert_eq!(CheckedNeg::checked_neg(1_u32), None);
193    /// ```
194    fn checked_neg(self) -> Option<Self::Output>;
195}
196}
197
198macro_rules! checked_neg_impl {
199    ($($t:ty)*) => {$(
200        c0nst::c0nst! {
201        c0nst impl CheckedNeg for $t {
202            type Output = $t;
203            #[inline]
204            fn checked_neg(self) -> Option<$t> {
205                <$t>::checked_neg(self)
206            }
207        }
208        }
209    )*};
210}
211
212checked_neg_impl!(u8 u16 u32 u64 usize u128 i8 i16 i32 i64 isize i128);
213
214c0nst::c0nst! {
215/// Performs shift left, returning `None` on shifts larger than or equal to
216/// the type width.
217pub c0nst trait CheckedShl: Sized + [c0nst] Shl<u32> {
218    /// Checked shift left. Computes `self << rhs`, returning `None`
219    /// if `rhs` is larger than or equal to the number of bits in `self`.
220    ///
221    /// ```
222    /// use const_num_traits::CheckedShl;
223    ///
224    /// let x: u16 = 0x0001;
225    ///
226    /// assert_eq!(CheckedShl::checked_shl(x, 0),  Some(0x0001));
227    /// assert_eq!(CheckedShl::checked_shl(x, 1),  Some(0x0002));
228    /// assert_eq!(CheckedShl::checked_shl(x, 15), Some(0x8000));
229    /// assert_eq!(CheckedShl::checked_shl(x, 16), None);
230    /// ```
231    fn checked_shl(self, rhs: u32) -> Option<<Self as Shl<u32>>::Output>;
232}
233}
234
235macro_rules! checked_shift_impl {
236    ($trait_name:ident, $method:ident, $t:ty) => {
237        c0nst::c0nst! {
238        c0nst impl $trait_name for $t {
239            #[inline]
240            fn $method(self, rhs: u32) -> Option<$t> {
241                <$t>::$method(self, rhs)
242            }
243        }
244        }
245    };
246}
247
248checked_shift_impl!(CheckedShl, checked_shl, u8);
249checked_shift_impl!(CheckedShl, checked_shl, u16);
250checked_shift_impl!(CheckedShl, checked_shl, u32);
251checked_shift_impl!(CheckedShl, checked_shl, u64);
252checked_shift_impl!(CheckedShl, checked_shl, usize);
253checked_shift_impl!(CheckedShl, checked_shl, u128);
254
255checked_shift_impl!(CheckedShl, checked_shl, i8);
256checked_shift_impl!(CheckedShl, checked_shl, i16);
257checked_shift_impl!(CheckedShl, checked_shl, i32);
258checked_shift_impl!(CheckedShl, checked_shl, i64);
259checked_shift_impl!(CheckedShl, checked_shl, isize);
260checked_shift_impl!(CheckedShl, checked_shl, i128);
261
262c0nst::c0nst! {
263/// Performs shift right, returning `None` on shifts larger than or equal to
264/// the type width.
265pub c0nst trait CheckedShr: Sized + [c0nst] Shr<u32> {
266    /// Checked shift right. Computes `self >> rhs`, returning `None`
267    /// if `rhs` is larger than or equal to the number of bits in `self`.
268    ///
269    /// ```
270    /// use const_num_traits::CheckedShr;
271    ///
272    /// let x: u16 = 0x8000;
273    ///
274    /// assert_eq!(CheckedShr::checked_shr(x, 0),  Some(0x8000));
275    /// assert_eq!(CheckedShr::checked_shr(x, 1),  Some(0x4000));
276    /// assert_eq!(CheckedShr::checked_shr(x, 15), Some(0x0001));
277    /// assert_eq!(CheckedShr::checked_shr(x, 16), None);
278    /// ```
279    fn checked_shr(self, rhs: u32) -> Option<<Self as Shr<u32>>::Output>;
280}
281}
282
283checked_shift_impl!(CheckedShr, checked_shr, u8);
284checked_shift_impl!(CheckedShr, checked_shr, u16);
285checked_shift_impl!(CheckedShr, checked_shr, u32);
286checked_shift_impl!(CheckedShr, checked_shr, u64);
287checked_shift_impl!(CheckedShr, checked_shr, usize);
288checked_shift_impl!(CheckedShr, checked_shr, u128);
289
290checked_shift_impl!(CheckedShr, checked_shr, i8);
291checked_shift_impl!(CheckedShr, checked_shr, i16);
292checked_shift_impl!(CheckedShr, checked_shr, i32);
293checked_shift_impl!(CheckedShr, checked_shr, i64);
294checked_shift_impl!(CheckedShr, checked_shr, isize);
295checked_shift_impl!(CheckedShr, checked_shr, i128);
296
297c0nst::c0nst! {
298/// Computes the absolute value, returning `None` if it can't be represented.
299pub c0nst trait CheckedAbs: Sized + [c0nst] Neg {
300    /// Checked absolute value. Computes `self.abs()`, returning `None` if
301    /// `self == MIN` (the result can't be represented).
302    ///
303    /// # Examples
304    ///
305    /// ```
306    /// use const_num_traits::CheckedAbs;
307    ///
308    /// assert_eq!(CheckedAbs::checked_abs(-5i32), Some(5));
309    /// assert_eq!(CheckedAbs::checked_abs(i32::MIN), None);
310    /// ```
311    fn checked_abs(self) -> Option<<Self as Neg>::Output>;
312}
313}
314
315checked_impl_unary!(CheckedAbs, checked_abs, i8);
316checked_impl_unary!(CheckedAbs, checked_abs, i16);
317checked_impl_unary!(CheckedAbs, checked_abs, i32);
318checked_impl_unary!(CheckedAbs, checked_abs, i64);
319checked_impl_unary!(CheckedAbs, checked_abs, isize);
320checked_impl_unary!(CheckedAbs, checked_abs, i128);
321
322c0nst::c0nst! {
323/// Performs exponentiation, returning `None` if overflow occurred.
324pub c0nst trait CheckedPow: Sized + [c0nst] Mul<Self> {
325    /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if
326    /// overflow occurred.
327    ///
328    /// Note that `0⁰` returns `Some(1)`, matching the inherent
329    /// `checked_pow` on the primitive integer types.
330    ///
331    /// # Examples
332    ///
333    /// ```
334    /// use const_num_traits::CheckedPow;
335    ///
336    /// assert_eq!(CheckedPow::checked_pow(2i8, 4), Some(16));
337    /// assert_eq!(CheckedPow::checked_pow(7i8, 8), None);
338    /// assert_eq!(CheckedPow::checked_pow(0u32, 0), Some(1));
339    /// ```
340    fn checked_pow(self, exp: u32) -> Option<<Self as Mul<Self>>::Output>;
341}
342}
343
344checked_shift_impl!(CheckedPow, checked_pow, u8);
345checked_shift_impl!(CheckedPow, checked_pow, u16);
346checked_shift_impl!(CheckedPow, checked_pow, u32);
347checked_shift_impl!(CheckedPow, checked_pow, u64);
348checked_shift_impl!(CheckedPow, checked_pow, usize);
349checked_shift_impl!(CheckedPow, checked_pow, u128);
350
351checked_shift_impl!(CheckedPow, checked_pow, i8);
352checked_shift_impl!(CheckedPow, checked_pow, i16);
353checked_shift_impl!(CheckedPow, checked_pow, i32);
354checked_shift_impl!(CheckedPow, checked_pow, i64);
355checked_shift_impl!(CheckedPow, checked_pow, isize);
356checked_shift_impl!(CheckedPow, checked_pow, i128);
357
358#[test]
359fn test_checked_abs_pow() {
360    fn checked_abs<T: CheckedAbs + core::ops::Neg<Output = T>>(a: T) -> Option<T> {
361        a.checked_abs()
362    }
363    fn checked_pow<T: CheckedPow + core::ops::Mul<Output = T>>(a: T, exp: u32) -> Option<T> {
364        a.checked_pow(exp)
365    }
366    assert_eq!(checked_abs(-5i16), Some(5));
367    assert_eq!(checked_abs(i16::MIN), None);
368    assert_eq!(checked_pow(3u8, 5), Some(243));
369    assert_eq!(checked_pow(2u8, 8), None);
370    assert_eq!(checked_pow(-3i32, 3), Some(-27));
371}