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);