diem_infallible/
math.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8/// Utility macro for writing secure arithmetic operations in order to avoid
9/// integer overflows.
10///
11/// # Examples
12///
13/// ```
14/// # use crate::diem_infallible::checked;
15/// let a: i64 = 1;
16/// let b: i64 = 2;
17/// let c: i64 = 3;
18///
19/// assert_eq!(checked!(a + b).unwrap(), 3);
20/// assert_eq!(checked!(a + b + c).unwrap(), 6);
21///
22/// // When doing multiple different operations, it's important to use parentheses in order
23/// // to guarantee the order of operation!
24/// assert_eq!(checked!(a + ((b - c) * c)).unwrap(), -2);
25///
26/// // When using numeric literals, the compiler might not be able to infer the type properly,
27/// // so if it complains, just add the type to the number.
28/// assert_eq!(checked!(10_u32 / 2_u32).unwrap(), 5);
29/// assert_eq!(checked!(10_u32 * 2_u32).unwrap(), 20);
30/// assert_eq!(checked!(10_u32 - 2_u32).unwrap(), 8);
31/// assert_eq!(checked!(2_i32 - 10_i32).unwrap(), -8);
32/// assert_eq!(checked!(10_u32 + 2_u32).unwrap(), 12);
33///
34/// // Casts using `as` operator must appear within parenthesis
35/// assert_eq!(checked!(10_u32 + (2_u16 as u32)).unwrap(), 12);
36///
37/// assert_eq!(checked!(10_u32 / (1_u32 + 1_u32)).unwrap(), 5);
38/// assert_eq!(checked!(10_u32 * (1_u32 + 1_u32)).unwrap(), 20);
39/// assert_eq!(checked!(10_u32 - (1_u32 + 1_u32)).unwrap(), 8);
40/// assert_eq!(checked!(10_u32 + (1_u32 + 1_u32)).unwrap(), 12);
41///
42/// let max = u32::max_value();
43/// assert!(checked!(max + 1_u32).is_err());
44/// assert!(checked!(0_u32 - 1_u32).is_err());
45///
46/// # struct Foo {
47/// #    pub bar: i32
48/// # }
49/// # impl Foo {
50/// #    pub fn one() -> i32 {
51/// #         1
52/// #    }
53/// # }
54/// // When one of the operands is an associated function or member, due to limitations with the
55/// // macro syntax which disallows an `expr` to precede a `+` sign, make sure to wrap the expression
56/// // in parenthesis
57/// # let foo = Foo { bar: 1 };
58/// assert_eq!(checked!((foo.bar) + 1_i32).unwrap(), 2);
59/// assert_eq!(checked!(1_i32 + (Foo::one())).unwrap(), 2);
60/// ```
61#[macro_export]
62macro_rules! checked {
63    ($a:tt + $b:tt) => {{
64        $a.checked_add($b).ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} + {}", $a, $b)))
65    }};
66    ($a:tt - $b:tt) => {{
67        $a.checked_sub($b).ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} - {}", $a, $b)))
68    }};
69    ($a:tt * $b:tt) => {{
70        $a.checked_mul($b).ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} * {}", $a, $b)))
71    }};
72    ($a:tt / $b:tt) => {{
73        $a.checked_div($b).ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} / {}", $a, $b)))
74    }};
75    ($a:tt + $($tokens:tt)*) => {{
76        checked!( $($tokens)* ).and_then(|b| {
77            b.checked_add($a)
78                .ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} + {}", b, $a)))
79        })
80    }};
81    ($a:tt - $($tokens:tt)*) => {{
82        checked!( $($tokens)* ).and_then(|b| {
83            b.checked_sub($a)
84                .ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} - {}", b, $a)))
85        })
86    }};
87    ($a:tt * $($tokens:tt)*) => {{
88        checked!( $($tokens)* ).and_then(|b| {
89            b.checked_mul($a)
90                .ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} * {}", b, $a)))
91        })
92    }};
93    ($a:tt / $($tokens:tt)*) => {{
94        checked!( $($tokens)* ).and_then(|b| {
95            b.checked_div($a)
96                .ok_or_else(|| $crate::ArithmeticError(format!("Operation results in overflow/underflow: {} / {}", b, $a)))
97        })
98    }};
99}
100
101#[derive(Debug)]
102pub struct ArithmeticError(pub String);
103
104impl std::fmt::Display for ArithmeticError {
105    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
106        write!(f, "{:?}", self.0)
107    }
108}
109
110impl std::error::Error for ArithmeticError {
111    fn description(&self) -> &str { &self.0 }
112}