oasis_core_runtime/common/
quantity.rs1use 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#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct Quantity(BigUint);
15
16impl Quantity {
17 #[inline]
19 pub fn checked_sub(&self, other: &Quantity) -> Option<Quantity> {
20 self.0.checked_sub(&other.0).map(Quantity)
23 }
24
25 #[inline]
28 pub fn checked_div(&self, other: &Quantity) -> Option<Quantity> {
29 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 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 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 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 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 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}