oasis_core_runtime/common/
quantity.rs

1//! An arbitrary precision unsigned integer.
2use std::{
3    convert::TryFrom,
4    fmt,
5    num::IntErrorKind,
6    ops::{Add, AddAssign, Mul, MulAssign},
7};
8
9use num_bigint::BigUint;
10use num_traits::{CheckedDiv, CheckedSub, ToPrimitive, Zero};
11
12/// An arbitrary precision unsigned integer.
13#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct Quantity(BigUint);
15
16impl Quantity {
17    /// Subtracts two numbers, checking for underflow. If underflow happens, `None` is returned.
18    #[inline]
19    pub fn checked_sub(&self, other: &Quantity) -> Option<Quantity> {
20        // NOTE: This does not implemented the num_traits::CheckedSub trait because this forces
21        //       one to also implement Sub which we explicitly don't want to do.
22        self.0.checked_sub(&other.0).map(Quantity)
23    }
24
25    /// Divides two numbers, checking for underflow, overflow and division by zero. If any of that
26    /// happens, `None` is returned.
27    #[inline]
28    pub fn checked_div(&self, other: &Quantity) -> Option<Quantity> {
29        // NOTE: This does not implemented the num_traits::CheckedDiv trait because this forces
30        //       one to also implement Div which we explicitly don't want to do.
31        self.0.checked_div(&other.0).map(Quantity)
32    }
33}
34
35impl Zero for Quantity {
36    fn zero() -> Self {
37        Quantity(BigUint::zero())
38    }
39
40    fn is_zero(&self) -> bool {
41        self.0.is_zero()
42    }
43}
44
45impl From<u8> for Quantity {
46    fn from(v: u8) -> Quantity {
47        Quantity(BigUint::from(v))
48    }
49}
50
51impl From<u16> for Quantity {
52    fn from(v: u16) -> Quantity {
53        Quantity(BigUint::from(v))
54    }
55}
56
57impl From<u32> for Quantity {
58    fn from(v: u32) -> Quantity {
59        Quantity(BigUint::from(v))
60    }
61}
62
63impl From<u64> for Quantity {
64    fn from(v: u64) -> Quantity {
65        Quantity(BigUint::from(v))
66    }
67}
68
69impl From<u128> for Quantity {
70    fn from(v: u128) -> Quantity {
71        Quantity(BigUint::from(v))
72    }
73}
74
75impl TryFrom<Quantity> for u64 {
76    type Error = IntErrorKind;
77
78    fn try_from(value: Quantity) -> Result<u64, Self::Error> {
79        value.0.to_u64().ok_or(IntErrorKind::PosOverflow)
80    }
81}
82
83impl TryFrom<&Quantity> for u64 {
84    type Error = IntErrorKind;
85
86    fn try_from(value: &Quantity) -> Result<u64, Self::Error> {
87        value.0.to_u64().ok_or(IntErrorKind::PosOverflow)
88    }
89}
90
91impl TryFrom<Quantity> for u128 {
92    type Error = IntErrorKind;
93
94    fn try_from(value: Quantity) -> Result<u128, Self::Error> {
95        value.0.to_u128().ok_or(IntErrorKind::PosOverflow)
96    }
97}
98
99impl TryFrom<&Quantity> for u128 {
100    type Error = IntErrorKind;
101
102    fn try_from(value: &Quantity) -> Result<u128, Self::Error> {
103        value.0.to_u128().ok_or(IntErrorKind::PosOverflow)
104    }
105}
106
107impl Add for Quantity {
108    type Output = Quantity;
109
110    fn add(mut self, other: Quantity) -> Quantity {
111        self += &other;
112        self
113    }
114}
115
116impl<'a> Add<&'a Quantity> for Quantity {
117    type Output = Quantity;
118
119    fn add(mut self, other: &Quantity) -> Quantity {
120        self += other;
121        self
122    }
123}
124
125impl<'a> AddAssign<&'a Quantity> for Quantity {
126    fn add_assign(&mut self, other: &Quantity) {
127        self.0 += &other.0;
128    }
129}
130
131impl AddAssign<Quantity> for Quantity {
132    fn add_assign(&mut self, other: Quantity) {
133        self.0 += other.0;
134    }
135}
136
137impl Add<u64> for Quantity {
138    type Output = Quantity;
139
140    fn add(mut self, other: u64) -> Quantity {
141        self += other;
142        self
143    }
144}
145
146impl AddAssign<u64> for Quantity {
147    fn add_assign(&mut self, other: u64) {
148        self.0 += other;
149    }
150}
151
152impl Mul for Quantity {
153    type Output = Quantity;
154
155    fn mul(mut self, rhs: Quantity) -> Quantity {
156        self *= &rhs;
157        self
158    }
159}
160
161impl<'a> Mul<&'a Quantity> for Quantity {
162    type Output = Quantity;
163
164    fn mul(mut self, rhs: &Quantity) -> Quantity {
165        self *= rhs;
166        self
167    }
168}
169
170impl<'a> MulAssign<&'a Quantity> for Quantity {
171    fn mul_assign(&mut self, rhs: &Quantity) {
172        self.0 *= &rhs.0;
173    }
174}
175
176impl MulAssign<Quantity> for Quantity {
177    fn mul_assign(&mut self, rhs: Quantity) {
178        self.0 *= rhs.0;
179    }
180}
181
182impl Mul<u64> for Quantity {
183    type Output = Quantity;
184
185    fn mul(mut self, other: u64) -> Quantity {
186        self *= other;
187        self
188    }
189}
190
191impl MulAssign<u64> for Quantity {
192    fn mul_assign(&mut self, other: u64) {
193        self.0 *= other;
194    }
195}
196
197impl fmt::Display for Quantity {
198    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199        self.0.fmt(f)
200    }
201}
202
203impl cbor::Encode for Quantity {
204    fn is_empty(&self) -> bool {
205        self.0.is_zero()
206    }
207
208    fn into_cbor_value(self) -> cbor::Value {
209        if self.0.is_zero() {
210            cbor::Value::ByteString(vec![])
211        } else {
212            cbor::Value::ByteString(self.0.to_bytes_be())
213        }
214    }
215}
216
217impl cbor::Decode for Quantity {
218    fn try_default() -> Result<Self, cbor::DecodeError> {
219        Ok(Default::default())
220    }
221
222    fn try_from_cbor_value(value: cbor::Value) -> Result<Self, cbor::DecodeError> {
223        match value {
224            cbor::Value::ByteString(data) => Ok(Quantity(BigUint::from_bytes_be(&data))),
225            _ => Err(cbor::DecodeError::UnexpectedType),
226        }
227    }
228}
229
230#[cfg(test)]
231mod test {
232    use rustc_hex::ToHex;
233
234    use crate::common::quantity::Quantity;
235
236    #[test]
237    fn test_serialization() {
238        // NOTE: These should be synced with go/common/quantity/quantity_test.go.
239        let cases = vec![
240            (0u128, "40"),
241            (1, "4101"),
242            (10, "410a"),
243            (100, "4164"),
244            (1000, "4203e8"),
245            (1000000, "430f4240"),
246            (18446744073709551615, "48ffffffffffffffff"),
247        ];
248
249        for tc in cases {
250            let q = Quantity::from(tc.0);
251            let enc = cbor::to_vec(q.clone());
252            assert_eq!(enc.to_hex::<String>(), tc.1, "serialization should match");
253
254            let dec: Quantity = cbor::from_slice(&enc).expect("deserialization should succeed");
255            assert_eq!(dec, q, "serialization should round-trip");
256        }
257    }
258
259    #[test]
260    fn test_ops() {
261        // Add.
262        assert_eq!(
263            Quantity::from(1000u32) + Quantity::from(2000u32),
264            Quantity::from(3000u32)
265        );
266
267        let mut a = Quantity::from(1000u32);
268        a += Quantity::from(42u32);
269        assert_eq!(a, Quantity::from(1042u32));
270        a += &Quantity::from(42u32);
271        assert_eq!(a, Quantity::from(1084u32));
272
273        let mut a = Quantity::from(1000u32);
274        a += 42;
275        assert_eq!(a, Quantity::from(1042u32));
276
277        // Sub.
278        let a = Quantity::from(1000u32);
279        assert_eq!(
280            a.checked_sub(&Quantity::from(42u32)),
281            Some(Quantity::from(958u32))
282        );
283        assert_eq!(a.checked_sub(&Quantity::from(1100u32)), None);
284
285        // Mul.
286        assert_eq!(
287            Quantity::from(1000u32) * Quantity::from(1000u32),
288            Quantity::from(1_000_000u32)
289        );
290
291        let mut a = Quantity::from(1000u32);
292        a *= Quantity::from(1000u32);
293        assert_eq!(a, Quantity::from(1_000_000u32));
294        a *= &Quantity::from(1000u32);
295        assert_eq!(a, Quantity::from(1_000_000_000u32));
296
297        let mut a = Quantity::from(1000u32);
298        a *= 1000;
299        assert_eq!(a, Quantity::from(1_000_000u32));
300
301        // Div.
302        let a = Quantity::from(1000u32);
303        assert_eq!(
304            a.checked_div(&Quantity::from(3u32)),
305            Some(Quantity::from(333u32))
306        );
307        assert_eq!(
308            a.checked_div(&Quantity::from(1001u32)),
309            Some(Quantity::from(0u32))
310        );
311        assert_eq!(a.checked_div(&Quantity::from(0u32)), None);
312    }
313}