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