Skip to main content

bnum/buint/
endian.rs

1use crate::digit;
2use crate::doc;
3// use core::mem::MaybeUninit;
4
5macro_rules! endian {
6    ($BUint: ident, $BInt: ident, $Digit: ident) => {
7        #[doc = doc::endian::impl_desc!($BUint)]
8        impl<const N: usize> $BUint<N> {
9            #[doc = doc::endian::from_be!(U 256)]
10            #[must_use]
11            #[inline]
12            pub const fn from_be(x: Self) -> Self {
13                #[cfg(target_endian = "big")]
14                return x;
15                #[cfg(not(target_endian = "big"))]
16                x.swap_bytes()
17            }
18
19            #[doc = doc::endian::from_le!(U 256)]
20            #[must_use]
21            #[inline]
22            pub const fn from_le(x: Self) -> Self {
23                #[cfg(target_endian = "little")]
24                return x;
25                #[cfg(not(target_endian = "little"))]
26                x.swap_bytes()
27            }
28
29            #[doc = doc::endian::to_be!(U 256)]
30            #[must_use = doc::must_use_op!()]
31            #[inline]
32            pub const fn to_be(self) -> Self {
33                Self::from_be(self)
34            }
35
36            #[doc = doc::endian::to_le!(U 256)]
37            #[must_use = doc::must_use_op!()]
38            #[inline]
39            pub const fn to_le(self) -> Self {
40                Self::from_le(self)
41            }
42
43            /// Create an integer value from a slice of bytes in big endian. The value is wrapped in an `Option` as the integer represented by the slice of bytes may represent an integer too large to be represented by the type.
44            ///
45            /// If the length of the slice is shorter than `Self::BYTES`, the slice is padded with zeros at the start so that it's length equals `Self::BYTES`.
46            ///
47            /// If the length of the slice is longer than `Self::BYTES`, `None` will be returned, unless leading zeros from the slice can be removed until the length of the slice equals `Self::BYTES`.
48            ///
49            /// # Examples
50            ///
51            /// ```
52            /// use bnum::types::U128;
53            ///
54            /// let value_from_array = U128::from(u128::from_be_bytes([0, 0, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]));
55            /// // using the `from_be_bytes` method on the primitve `u128` here instead of on `U128` as `from_be_bytes` is currently only available in bnum on nightly
56            /// let value_from_slice = U128::from_be_slice(&[0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]).unwrap();
57            /// let value_from_long_slice = U128::from_be_slice(&[0, 0, 0, 0, 0, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12]).unwrap();
58            ///
59            /// assert_eq!(value_from_array, value_from_slice);
60            /// assert_eq!(value_from_array, value_from_long_slice);
61            ///
62            /// let invalid_slice = &[0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90];
63            /// assert_eq!(U128::from_be_slice(invalid_slice), None);
64            /// ```
65            #[must_use]
66            pub const fn from_be_slice(slice: &[u8]) -> Option<Self> {
67                let len = slice.len();
68                let mut out = Self::ZERO;
69                // let slice_ptr = slice.as_ptr();
70                let mut i = 0;
71                let exact = len >> digit::$Digit::BYTE_SHIFT;
72                while i < exact {
73                    let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize];
74                    let init_index = len - digit::$Digit::BYTES as usize;
75                    let mut j = init_index;
76                    while j < slice.len() {
77                        digit_bytes[j - init_index] = slice[j - (i << digit::$Digit::BYTE_SHIFT)];
78                        j += 1;
79                    }
80                    let digit = $Digit::from_be_bytes(digit_bytes);
81                    if i < N {
82                        out.digits[i] = digit;
83                    } else if digit != 0 {
84                        return None;
85                    };
86                    i += 1;
87                }
88                let rem = len & (digit::$Digit::BYTES as usize - 1);
89                if rem == 0 {
90                    Some(out)
91                } else {
92                    let mut last_digit_bytes = [0; digit::$Digit::BYTES as usize];
93                    let mut j = 0;
94                    while j < rem {
95                        last_digit_bytes[digit::$Digit::BYTES as usize - rem + j] = slice[j];
96                        j += 1;
97                    }
98                    let digit = $Digit::from_be_bytes(last_digit_bytes);
99                    if i < N {
100                        out.digits[i] = digit;
101                    } else if digit != 0 {
102                        return None;
103                    };
104                    Some(out)
105                }
106            }
107
108            /// Creates an integer value from a slice of bytes in little endian. The value is wrapped in an `Option` as the bytes may represent an integer too large to be represented by the type.
109            ///
110            /// If the length of the slice is shorter than `Self::BYTES`, the slice is padded with zeros at the end so that it's length equals `Self::BYTES`.
111            ///
112            /// If the length of the slice is longer than `Self::BYTES`, `None` will be returned, unless trailing zeros from the slice can be removed until the length of the slice equals `Self::BYTES`.
113            ///
114            /// # Examples
115            ///
116            /// ```
117            /// use bnum::types::U128;
118            ///
119            /// let value_from_array = U128::from(u128::from_le_bytes([0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0, 0]));
120            /// // using the `from_le_bytes` method on the primitve `u128` here instead of on `U128` as `from_le_bytes` is currently only available in bnum on nightly
121            /// let value_from_slice = U128::from_le_slice(&[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56]).unwrap();
122            /// let value_from_long_slice = U128::from_le_slice(&[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0, 0, 0, 0, 0, 0]).unwrap();
123            ///
124            /// assert_eq!(value_from_array, value_from_slice);
125            /// assert_eq!(value_from_array, value_from_long_slice);
126            ///
127            /// let invalid_slice = &[0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90];
128            /// assert_eq!(U128::from_le_slice(invalid_slice), None);
129            /// ```
130            #[must_use]
131            pub const fn from_le_slice(slice: &[u8]) -> Option<Self> {
132                let len = slice.len();
133                let mut out = Self::ZERO;
134                // let slice_ptr = slice.as_ptr();
135                let mut i = 0;
136                let exact = len >> digit::$Digit::BYTE_SHIFT;
137                while i < exact {
138                    let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize];
139                    let init_index = i << digit::$Digit::BYTE_SHIFT;
140                    let mut j = init_index;
141                    while j < init_index + digit::$Digit::BYTES as usize {
142                        digit_bytes[j - init_index] = slice[j];
143                        j += 1;
144                    }
145                    let digit = $Digit::from_le_bytes(digit_bytes);
146                    if i < N {
147                        out.digits[i] = digit;
148                    } else if digit != 0 {
149                        return None;
150                    };
151                    i += 1;
152                }
153                if len & (digit::$Digit::BYTES as usize - 1) == 0 {
154                    Some(out)
155                } else {
156                    let mut last_digit_bytes = [0; digit::$Digit::BYTES as usize];
157                    let addition = exact << digit::$Digit::BYTE_SHIFT;
158                    let mut j = 0;
159                    while j + addition < len {
160                        last_digit_bytes[j] = slice[j + addition];
161                        j += 1;
162                    }
163                    let digit = $Digit::from_le_bytes(last_digit_bytes);
164                    if i < N {
165                        out.digits[i] = digit;
166                    } else if digit != 0 {
167                        return None;
168                    };
169                    Some(out)
170                }
171            }
172
173            #[cfg(feature = "nightly")]
174            #[doc = doc::endian::to_be_bytes!(U)]
175            #[doc = doc::requires_feature!("nightly")]
176            #[must_use = doc::must_use_op!()]
177            #[inline]
178            pub const fn to_be_bytes(self) -> [u8; Self::BYTES_USIZE] {
179                let mut bytes = [0; Self::BYTES_USIZE];
180                let mut i = N;
181                while i > 0 {
182                    let digit_bytes = self.digits[N - i].to_be_bytes();
183                    i -= 1;
184                    let mut j = 0;
185                    while j < digit::$Digit::BYTES as usize {
186                        bytes[(i << digit::$Digit::BYTE_SHIFT) + j] = digit_bytes[j];
187                        j += 1;
188                    }
189                }
190                bytes
191            }
192
193            #[cfg(feature = "nightly")]
194            #[doc = doc::endian::to_le_bytes!(U)]
195            #[doc = doc::requires_feature!("nightly")]
196            #[must_use = doc::must_use_op!()]
197            #[inline]
198            pub const fn to_le_bytes(self) -> [u8; Self::BYTES_USIZE] {
199                // Strangely, this is slightly faster than direct transmutation by either `mem::transmute_copy` or `ptr::read`.
200                // Also, initialising the bytes with zeros is faster than using MaybeUninit.
201                // The Rust compiler is probably being very smart and optimizing this code.
202                // The same goes for `to_be_bytes`.
203                let mut bytes = [0; Self::BYTES_USIZE];
204                let mut i = 0;
205                while i < N {
206                    let digit_bytes = self.digits[i].to_le_bytes();
207                    let mut j = 0;
208                    while j < digit::$Digit::BYTES as usize {
209                        bytes[(i << digit::$Digit::BYTE_SHIFT) + j] = digit_bytes[j];
210                        j += 1;
211                    }
212                    i += 1;
213                }
214                bytes
215            }
216
217            #[cfg(feature = "nightly")]
218            #[doc = doc::endian::to_ne_bytes!(U)]
219            #[doc = doc::requires_feature!("nightly")]
220            #[must_use = doc::must_use_op!()]
221            #[inline]
222            pub const fn to_ne_bytes(self) -> [u8; Self::BYTES_USIZE] {
223                #[cfg(target_endian = "big")]
224                return self.to_be_bytes();
225                #[cfg(not(target_endian = "big"))]
226                self.to_le_bytes()
227            }
228
229            #[cfg(feature = "nightly")]
230            #[doc = doc::endian::from_be_bytes!(U)]
231            #[doc = doc::requires_feature!("nightly")]
232            #[must_use]
233            #[inline]
234            pub const fn from_be_bytes(bytes: [u8; Self::BYTES_USIZE]) -> Self {
235                let mut out = Self::ZERO;
236                // let arr_ptr = bytes.as_ptr();
237                let mut i = 0;
238                while i < N {
239                    let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize];
240                    let init_index = N * digit::$Digit::BYTES as usize - digit::$Digit::BYTES as usize;
241                    let mut j = init_index;
242                    while j < N * digit::$Digit::BYTES as usize {
243                        digit_bytes[j - init_index] = bytes[j - (i << digit::$Digit::BYTE_SHIFT)];
244                        j += 1;
245                    }
246                    out.digits[i] = $Digit::from_be_bytes(digit_bytes);
247                    i += 1;
248                }
249                out
250            }
251
252            #[cfg(feature = "nightly")]
253            #[doc = doc::endian::from_le_bytes!(U)]
254            #[doc = doc::requires_feature!("nightly")]
255            #[must_use]
256            #[inline]
257            pub const fn from_le_bytes(bytes: [u8; Self::BYTES_USIZE]) -> Self {
258                let mut out = Self::ZERO;
259                // let arr_ptr = bytes.as_ptr();
260                let mut i = 0;
261                while i < N {
262                    let mut digit_bytes = [0u8; digit::$Digit::BYTES as usize];
263                    let init_index = i << digit::$Digit::BYTE_SHIFT;
264                    let mut j = init_index;
265                    while j < init_index + digit::$Digit::BYTES as usize {
266                        digit_bytes[j - init_index] = bytes[j];
267                        j += 1;
268                    }
269                    out.digits[i] = $Digit::from_le_bytes(digit_bytes);
270                    i += 1;
271                }
272                out
273            }
274
275            #[cfg(feature = "nightly")]
276            #[doc = doc::endian::from_ne_bytes!(U)]
277            #[doc = doc::requires_feature!("nightly")]
278            #[must_use]
279            #[inline]
280            pub const fn from_ne_bytes(bytes: [u8; Self::BYTES_USIZE]) -> Self {
281                #[cfg(target_endian = "big")]
282                return Self::from_be_bytes(bytes);
283
284                #[cfg(not(target_endian = "big"))]
285                Self::from_le_bytes(bytes)
286            }
287
288            pub(crate) const BYTES_USIZE: usize = N * digit::$Digit::BYTES as usize;
289        }
290    };
291}
292
293#[cfg(test)]
294crate::test::all_digit_tests! {
295    use crate::test::{test_bignum, types::utest};
296
297    crate::int::endian::tests!(utest);
298}
299
300crate::macro_impl!(endian);