fuel_types/
numeric_types.rs

1use core::{
2    array::TryFromSliceError,
3    borrow::{
4        Borrow,
5        BorrowMut,
6    },
7    convert::TryFrom,
8    fmt,
9    ops::{
10        Deref,
11        DerefMut,
12    },
13    str,
14};
15
16#[cfg(feature = "random")]
17use rand::{
18    distributions::{
19        Distribution,
20        Standard,
21    },
22    Rng,
23};
24
25#[cfg(all(feature = "alloc", feature = "typescript"))]
26use alloc::vec::Vec;
27
28macro_rules! key {
29    ($i:ident, $t:ty) => {
30        #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31        /// FuelVM atomic numeric type.
32        #[repr(transparent)]
33        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34        #[cfg_attr(feature = "serde", serde(transparent))]
35        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
36        #[derive(
37            fuel_types::canonical::Serialize, fuel_types::canonical::Deserialize,
38        )]
39        pub struct $i($t);
40
41        key_methods!($i, $t);
42
43        #[cfg(feature = "random")]
44        impl Distribution<$i> for Standard {
45            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $i {
46                $i(rng.gen())
47            }
48        }
49    };
50}
51
52macro_rules! key_methods {
53    ($i:ident, $t:ty) => {
54        const _: () = {
55            const SIZE: usize = core::mem::size_of::<$t>();
56
57            impl $i {
58                /// Number constructor.
59                pub const fn new(number: $t) -> Self {
60                    Self(number)
61                }
62
63                /// Convert to array of big endian bytes.
64                pub fn to_bytes(self) -> [u8; SIZE] {
65                    self.0.to_be_bytes()
66                }
67            }
68
69            #[cfg(feature = "typescript")]
70            #[wasm_bindgen::prelude::wasm_bindgen]
71            impl $i {
72                #[wasm_bindgen::prelude::wasm_bindgen(constructor)]
73                /// Number constructor.
74                pub fn from_number(number: $t) -> Self {
75                    Self(number)
76                }
77
78                /// Convert to array of big endian bytes.
79                #[wasm_bindgen(js_name = to_bytes)]
80                pub fn to_bytes_typescript(self) -> Vec<u8> {
81                    self.to_bytes().to_vec()
82                }
83
84                /// Convert to usize.
85                #[wasm_bindgen(js_name = as_usize)]
86                pub fn as_usize_typescript(&self) -> usize {
87                    usize::try_from(self.0).expect("Cannot convert to usize")
88                }
89            }
90
91            #[cfg(feature = "random")]
92            impl rand::Fill for $i {
93                fn try_fill<R: rand::Rng + ?Sized>(
94                    &mut self,
95                    rng: &mut R,
96                ) -> Result<(), rand::Error> {
97                    let number = rng.gen();
98                    *self = $i(number);
99
100                    Ok(())
101                }
102            }
103
104            impl Deref for $i {
105                type Target = $t;
106
107                fn deref(&self) -> &$t {
108                    &self.0
109                }
110            }
111
112            impl Borrow<$t> for $i {
113                fn borrow(&self) -> &$t {
114                    &self.0
115                }
116            }
117
118            impl BorrowMut<$t> for $i {
119                fn borrow_mut(&mut self) -> &mut $t {
120                    &mut self.0
121                }
122            }
123
124            impl DerefMut for $i {
125                fn deref_mut(&mut self) -> &mut $t {
126                    &mut self.0
127                }
128            }
129
130            impl From<[u8; SIZE]> for $i {
131                fn from(bytes: [u8; SIZE]) -> Self {
132                    Self(<$t>::from_be_bytes(bytes))
133                }
134            }
135
136            impl From<$t> for $i {
137                fn from(value: $t) -> Self {
138                    Self(value)
139                }
140            }
141
142            impl From<$i> for [u8; SIZE] {
143                fn from(salt: $i) -> [u8; SIZE] {
144                    salt.0.to_be_bytes()
145                }
146            }
147
148            impl From<$i> for $t {
149                fn from(salt: $i) -> $t {
150                    salt.0
151                }
152            }
153
154            impl TryFrom<&[u8]> for $i {
155                type Error = TryFromSliceError;
156
157                fn try_from(bytes: &[u8]) -> Result<$i, TryFromSliceError> {
158                    <[u8; SIZE]>::try_from(bytes).map(|b| b.into())
159                }
160            }
161
162            impl fmt::LowerHex for $i {
163                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164                    if f.alternate() {
165                        write!(f, "0x")?
166                    }
167
168                    let bytes = self.0.to_be_bytes();
169                    match f.width() {
170                        Some(w) if w > 0 => {
171                            bytes.chunks(2 * bytes.len() / w).try_for_each(|c| {
172                                write!(f, "{:02x}", c.iter().fold(0u8, |acc, x| acc ^ x))
173                            })
174                        }
175
176                        _ => bytes.iter().try_for_each(|b| write!(f, "{:02x}", &b)),
177                    }
178                }
179            }
180
181            impl fmt::UpperHex for $i {
182                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183                    if f.alternate() {
184                        write!(f, "0x")?
185                    }
186
187                    let bytes = self.0.to_be_bytes();
188                    match f.width() {
189                        Some(w) if w > 0 => {
190                            bytes.chunks(2 * bytes.len() / w).try_for_each(|c| {
191                                write!(f, "{:02X}", c.iter().fold(0u8, |acc, x| acc ^ x))
192                            })
193                        }
194
195                        _ => bytes.iter().try_for_each(|b| write!(f, "{:02X}", &b)),
196                    }
197                }
198            }
199
200            impl fmt::Debug for $i {
201                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202                    <Self as fmt::LowerHex>::fmt(&self, f)
203                }
204            }
205
206            impl fmt::Display for $i {
207                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208                    <Self as fmt::LowerHex>::fmt(&self, f)
209                }
210            }
211
212            impl str::FromStr for $i {
213                type Err = &'static str;
214
215                fn from_str(s: &str) -> Result<Self, Self::Err> {
216                    const ERR: &str = concat!("Invalid encoded byte in ", stringify!($i));
217                    let mut ret = <[u8; SIZE]>::default();
218                    let s = s.strip_prefix("0x").unwrap_or(s);
219                    hex::decode_to_slice(&s, &mut ret).map_err(|_| ERR)?;
220                    Ok(ret.into())
221                }
222            }
223        };
224    };
225}
226
227key!(BlockHeight, u32);
228key!(ChainId, u64);
229
230impl BlockHeight {
231    /// Successor, i.e. next block after this
232    pub fn succ(self) -> Option<BlockHeight> {
233        Some(Self(self.0.checked_add(1)?))
234    }
235
236    /// Predecessor, i.e. previous block before this
237    pub fn pred(self) -> Option<BlockHeight> {
238        Some(Self(self.0.checked_sub(1)?))
239    }
240}