ruint/
bytes.rs

1// OPT: Use u64::from_{be/le}_bytes() to work 8 bytes at a time.
2// FEATURE: (BLOCKED) Make `const fn`s when `const_for` is stable.
3
4use crate::Uint;
5use core::slice;
6
7#[cfg(feature = "alloc")]
8#[allow(unused_imports)]
9use alloc::{borrow::Cow, vec::Vec};
10
11// OPT: *_to_smallvec to avoid allocation.
12impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
13    /// The size of this integer type in bytes. Note that some bits may be
14    /// forced zero if BITS is not cleanly divisible by eight.
15    pub const BYTES: usize = (BITS + 7) / 8;
16
17    /// Access the underlying store as a little-endian slice of bytes.
18    ///
19    /// Only available on little-endian targets.
20    ///
21    /// If `BITS` does not evenly divide 8, it is padded with zero bits in the
22    /// most significant position.
23    #[cfg(target_endian = "little")]
24    #[must_use]
25    #[inline(always)]
26    pub const fn as_le_slice(&self) -> &[u8] {
27        unsafe { slice::from_raw_parts(self.limbs.as_ptr().cast(), Self::BYTES) }
28    }
29
30    /// Access the underlying store as a mutable little-endian slice of bytes.
31    ///
32    /// Only available on litte-endian targets.
33    ///
34    /// # Safety
35    ///
36    /// If `BITS` does not evenly divide 8, it is padded with zero bits in the
37    /// most significant position. Setting those bits puts the [`Uint`] in an
38    /// invalid state.
39    #[cfg(target_endian = "little")]
40    #[must_use]
41    #[inline(always)]
42    pub unsafe fn as_le_slice_mut(&mut self) -> &mut [u8] {
43        unsafe { slice::from_raw_parts_mut(self.limbs.as_mut_ptr().cast(), Self::BYTES) }
44    }
45
46    /// Access the underlying store as a little-endian bytes.
47    ///
48    /// Uses an optimized implementation on little-endian targets.
49    #[cfg(feature = "alloc")]
50    #[must_use]
51    #[inline]
52    #[allow(clippy::missing_const_for_fn)]
53    pub fn as_le_bytes(&self) -> Cow<'_, [u8]> {
54        // On little endian platforms this is a no-op.
55        #[cfg(target_endian = "little")]
56        return Cow::Borrowed(self.as_le_slice());
57
58        // In others, reverse each limb and return a copy.
59        #[cfg(target_endian = "big")]
60        return Cow::Owned({
61            let mut cpy = *self;
62            for limb in &mut cpy.limbs {
63                *limb = limb.reverse_bits();
64            }
65            unsafe { slice::from_raw_parts(cpy.limbs.as_ptr().cast(), Self::BYTES).to_vec() }
66        });
67    }
68
69    /// Access the underlying store as a little-endian bytes with trailing zeros
70    /// removed.
71    ///
72    /// Uses an optimized implementation on little-endian targets.
73    #[cfg(feature = "alloc")]
74    #[must_use]
75    #[inline]
76    pub fn as_le_bytes_trimmed(&self) -> Cow<'_, [u8]> {
77        match self.as_le_bytes() {
78            Cow::Borrowed(slice) => Cow::Borrowed(crate::utils::trim_end_slice(slice, &0)),
79            Cow::Owned(mut vec) => {
80                crate::utils::trim_end_vec(&mut vec, &0);
81                Cow::Owned(vec)
82            }
83        }
84    }
85
86    /// Converts the [`Uint`] to a little-endian byte array of size exactly
87    /// [`Self::BYTES`].
88    ///
89    /// # Panics
90    ///
91    /// Panics if the generic parameter `BYTES` is not exactly [`Self::BYTES`].
92    /// Ideally this would be a compile time error, but this is blocked by
93    /// Rust issue [#60551].
94    ///
95    /// [#60551]: https://github.com/rust-lang/rust/issues/60551
96    #[inline]
97    #[must_use]
98    pub const fn to_le_bytes<const BYTES: usize>(&self) -> [u8; BYTES] {
99        // TODO: Use a `const {}` block for this assertion
100        assert!(BYTES == Self::BYTES, "BYTES must be equal to Self::BYTES");
101
102        // Specialized impl
103        #[cfg(target_endian = "little")]
104        // SAFETY: BYTES == Self::BYTES == self.as_le_slice().len()
105        return unsafe { *self.as_le_slice().as_ptr().cast() };
106
107        // Generic impl
108        #[cfg(target_endian = "big")]
109        {
110            let mut limbs = self.limbs;
111            let mut i = 0;
112            while i < LIMBS {
113                limbs[i] = limbs[i].to_le();
114                i += 1;
115            }
116            // SAFETY: BYTES <= LIMBS * 8
117            unsafe { *limbs.as_ptr().cast() }
118        }
119    }
120
121    /// Converts the [`Uint`] to a little-endian byte vector of size exactly
122    /// [`Self::BYTES`].
123    ///
124    /// This method is useful when [`Self::to_le_bytes`] can not be used because
125    /// byte size is not known compile time.
126    #[cfg(feature = "alloc")]
127    #[must_use]
128    #[inline]
129    pub fn to_le_bytes_vec(&self) -> Vec<u8> {
130        self.as_le_bytes().into_owned()
131    }
132
133    /// Converts the [`Uint`] to a little-endian byte vector with trailing zeros
134    /// bytes removed.
135    #[cfg(feature = "alloc")]
136    #[must_use]
137    #[inline]
138    pub fn to_le_bytes_trimmed_vec(&self) -> Vec<u8> {
139        self.as_le_bytes_trimmed().into_owned()
140    }
141
142    /// Converts the [`Uint`] to a big-endian byte array of size exactly
143    /// [`Self::BYTES`].
144    ///
145    /// # Panics
146    ///
147    /// Panics if the generic parameter `BYTES` is not exactly [`Self::BYTES`].
148    /// Ideally this would be a compile time error, but this is blocked by
149    /// Rust issue [#60551].
150    ///
151    /// [#60551]: https://github.com/rust-lang/rust/issues/60551
152    #[must_use]
153    #[inline]
154    pub const fn to_be_bytes<const BYTES: usize>(&self) -> [u8; BYTES] {
155        let mut bytes = self.to_le_bytes::<BYTES>();
156
157        // bytes.reverse()
158        let len = bytes.len();
159        let half_len = len / 2;
160        let mut i = 0;
161        while i < half_len {
162            let tmp = bytes[i];
163            bytes[i] = bytes[len - 1 - i];
164            bytes[len - 1 - i] = tmp;
165            i += 1;
166        }
167
168        bytes
169    }
170
171    /// Converts the [`Uint`] to a big-endian byte vector of size exactly
172    /// [`Self::BYTES`].
173    ///
174    /// This method is useful when [`Self::to_be_bytes`] can not be used because
175    /// byte size is not known compile time.
176    #[cfg(feature = "alloc")]
177    #[must_use]
178    #[inline]
179    pub fn to_be_bytes_vec(&self) -> Vec<u8> {
180        let mut bytes = self.to_le_bytes_vec();
181        bytes.reverse();
182        bytes
183    }
184
185    /// Converts the [`Uint`] to a big-endian byte vector with leading zeros
186    /// bytes removed.
187    #[cfg(feature = "alloc")]
188    #[must_use]
189    #[inline]
190    pub fn to_be_bytes_trimmed_vec(&self) -> Vec<u8> {
191        let mut bytes = self.to_le_bytes_trimmed_vec();
192        bytes.reverse();
193        bytes
194    }
195
196    /// Converts a big-endian byte array of size exactly
197    /// [`Self::BYTES`] to [`Uint`].
198    ///
199    /// # Panics
200    ///
201    /// Panics if the generic parameter `BYTES` is not exactly [`Self::BYTES`].
202    /// Ideally this would be a compile time error, but this is blocked by
203    /// Rust issue [#60551].
204    ///
205    /// [#60551]: https://github.com/rust-lang/rust/issues/60551
206    ///
207    /// Panics if the value is too large for the bit-size of the Uint.
208    #[must_use]
209    #[track_caller]
210    #[inline]
211    pub const fn from_be_bytes<const BYTES: usize>(bytes: [u8; BYTES]) -> Self {
212        // TODO: Use a `const {}` block for this assertion
213        assert!(BYTES == Self::BYTES, "BYTES must be equal to Self::BYTES");
214        Self::from_be_slice(&bytes)
215    }
216
217    /// Creates a new integer from a big endian slice of bytes.
218    ///
219    /// The slice is interpreted as a big endian number. Leading zeros
220    /// are ignored. The slice can be any length.
221    ///
222    /// # Panics
223    ///
224    /// Panics if the value is larger than fits the [`Uint`].
225    #[must_use]
226    #[track_caller]
227    #[inline]
228    pub const fn from_be_slice(bytes: &[u8]) -> Self {
229        match Self::try_from_be_slice(bytes) {
230            Some(value) => value,
231            None => panic!("Value too large for Uint"),
232        }
233    }
234
235    /// Creates a new integer from a big endian slice of bytes.
236    ///
237    /// The slice is interpreted as a big endian number. Leading zeros
238    /// are ignored. The slice can be any length.
239    ///
240    /// Returns [`None`] if the value is larger than fits the [`Uint`].
241    #[must_use]
242    #[inline]
243    pub const fn try_from_be_slice(bytes: &[u8]) -> Option<Self> {
244        if bytes.len() > Self::BYTES {
245            return None;
246        }
247
248        if Self::BYTES % 8 == 0 && bytes.len() == Self::BYTES {
249            // Optimized implementation for full-limb types.
250            let mut limbs = [0; LIMBS];
251            let end = bytes.as_ptr_range().end;
252            let mut i = 0;
253            while i < LIMBS {
254                limbs[i] = u64::from_be_bytes(unsafe { *end.sub((i + 1) * 8).cast() });
255                i += 1;
256            }
257            return Some(Self::from_limbs(limbs));
258        }
259
260        let mut limbs = [0; LIMBS];
261        let mut i = 0;
262        let mut c = bytes.len();
263        while i < bytes.len() {
264            c -= 1;
265            limbs[i / 8] += (bytes[c] as u64) << ((i % 8) * 8);
266            i += 1;
267        }
268        if Self::LIMBS > 0 && limbs[Self::LIMBS - 1] > Self::MASK {
269            return None;
270        }
271        Some(Self::from_limbs(limbs))
272    }
273
274    /// Converts a little-endian byte array of size exactly
275    /// [`Self::BYTES`] to [`Uint`].
276    ///
277    /// # Panics
278    ///
279    /// Panics if the generic parameter `BYTES` is not exactly [`Self::BYTES`].
280    /// Ideally this would be a compile time error, but this is blocked by
281    /// Rust issue [#60551].
282    ///
283    /// [#60551]: https://github.com/rust-lang/rust/issues/60551
284    ///
285    /// Panics if the value is too large for the bit-size of the Uint.
286    #[must_use]
287    #[track_caller]
288    #[inline]
289    pub const fn from_le_bytes<const BYTES: usize>(bytes: [u8; BYTES]) -> Self {
290        // TODO: Use a `const {}` block for this assertion
291        assert!(BYTES == Self::BYTES, "BYTES must be equal to Self::BYTES");
292        Self::from_le_slice(&bytes)
293    }
294
295    /// Creates a new integer from a little endian slice of bytes.
296    ///
297    /// The slice is interpreted as a little endian number. Leading zeros
298    /// are ignored. The slice can be any length.
299    ///
300    /// # Panics
301    ///
302    /// Panics if the value is larger than fits the [`Uint`].
303    #[must_use]
304    #[track_caller]
305    #[inline]
306    pub const fn from_le_slice(bytes: &[u8]) -> Self {
307        match Self::try_from_le_slice(bytes) {
308            Some(value) => value,
309            None => panic!("Value too large for Uint"),
310        }
311    }
312
313    /// Creates a new integer from a little endian slice of bytes.
314    ///
315    /// The slice is interpreted as a little endian number. Leading zeros
316    /// are ignored. The slice can be any length.
317    ///
318    /// Returns [`None`] if the value is larger than fits the [`Uint`].
319    #[must_use]
320    #[inline]
321    pub const fn try_from_le_slice(bytes: &[u8]) -> Option<Self> {
322        if bytes.len() / 8 > Self::LIMBS {
323            return None;
324        }
325
326        if Self::BYTES % 8 == 0 && bytes.len() == Self::BYTES {
327            // Optimized implementation for full-limb types.
328            let mut limbs = [0; LIMBS];
329            let mut i = 0;
330            while i < LIMBS {
331                limbs[i] = u64::from_le_bytes(unsafe { *bytes.as_ptr().add(i * 8).cast() });
332                i += 1;
333            }
334            return Some(Self::from_limbs(limbs));
335        }
336
337        let mut limbs = [0; LIMBS];
338        let mut i = 0;
339        while i < bytes.len() {
340            limbs[i / 8] += (bytes[i] as u64) << ((i % 8) * 8);
341            i += 1;
342        }
343        if Self::LIMBS > 0 && limbs[Self::LIMBS - 1] > Self::MASK {
344            return None;
345        }
346        Some(Self::from_limbs(limbs))
347    }
348}
349
350/// Number of bytes required to represent the given number of bits.
351///
352/// This needs to be public because it is used in the `Uint` type,
353/// specifically in the [`to_be_bytes()`][Uint::to_be_bytes] and related
354/// functions.
355#[inline]
356#[must_use]
357pub const fn nbytes(bits: usize) -> usize {
358    (bits + 7) / 8
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364    use crate::{const_for, nlimbs};
365    use proptest::proptest;
366
367    const N: Uint<128, 2> =
368        Uint::from_limbs([0x7890_1234_5678_9012_u64, 0x1234_5678_9012_3456_u64]);
369    const BE: [u8; 16] = [
370        0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90,
371        0x12,
372    ];
373    const LE: [u8; 16] = [
374        0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34,
375        0x12,
376    ];
377
378    const K: Uint<72, 2> = Uint::from_limbs([0x3456_7890_1234_5678_u64, 0x12_u64]);
379    const KBE: [u8; 9] = [0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78];
380    const KLE: [u8; 9] = [0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12];
381
382    #[test]
383    const fn const_from_to_bytes() {
384        const NL: [u64; 2] = N.limbs;
385        const KL: [u64; 2] = K.limbs;
386        assert!(matches!(Uint::<128, 2>::from_be_bytes(BE).limbs, NL));
387        assert!(matches!(Uint::<128, 2>::from_le_bytes(LE).limbs, NL));
388        assert!(matches!(N.to_be_bytes::<{ BE.len() }>(), BE));
389        assert!(matches!(N.to_le_bytes::<{ LE.len() }>(), LE));
390
391        assert!(matches!(Uint::<72, 2>::from_be_bytes(KBE).limbs, KL));
392        assert!(matches!(Uint::<72, 2>::from_le_bytes(KLE).limbs, KL));
393        assert!(matches!(K.to_be_bytes::<{ KBE.len() }>(), KBE));
394        assert!(matches!(K.to_le_bytes::<{ KLE.len() }>(), KLE));
395
396        assert!(matches!(Uint::<0, 0>::ZERO.to_be_bytes::<0>(), []));
397        assert!(matches!(Uint::<1, 1>::ZERO.to_be_bytes::<1>(), [0]));
398        assert!(matches!(
399            Uint::<1, 1>::from_limbs([1]).to_be_bytes::<1>(),
400            [1]
401        ));
402        assert!(matches!(
403            Uint::<16, 1>::from_limbs([0x1234]).to_be_bytes::<2>(),
404            [0x12, 0x34]
405        ));
406
407        assert!(matches!(Uint::<0, 0>::ZERO.to_be_bytes::<0>(), []));
408        assert!(matches!(Uint::<0, 0>::ZERO.to_le_bytes::<0>(), []));
409        assert!(matches!(Uint::<1, 1>::ZERO.to_be_bytes::<1>(), [0]));
410        assert!(matches!(Uint::<1, 1>::ZERO.to_le_bytes::<1>(), [0]));
411        assert!(matches!(
412            Uint::<1, 1>::from_limbs([1]).to_be_bytes::<1>(),
413            [1]
414        ));
415        assert!(matches!(
416            Uint::<1, 1>::from_limbs([1]).to_le_bytes::<1>(),
417            [1]
418        ));
419        assert!(matches!(
420            Uint::<16, 1>::from_limbs([0x1234]).to_be_bytes::<2>(),
421            [0x12, 0x34]
422        ));
423        assert!(matches!(
424            Uint::<16, 1>::from_limbs([0x1234]).to_le_bytes::<2>(),
425            [0x34, 0x12]
426        ));
427
428        assert!(matches!(
429            Uint::<63, 1>::from_limbs([0x010203]).to_be_bytes::<8>(),
430            [0, 0, 0, 0, 0, 1, 2, 3]
431        ));
432        assert!(matches!(
433            Uint::<63, 1>::from_limbs([0x010203]).to_le_bytes::<8>(),
434            [3, 2, 1, 0, 0, 0, 0, 0]
435        ));
436    }
437
438    #[test]
439    fn test_from_bytes() {
440        assert_eq!(Uint::<0, 0>::from_be_bytes([]), Uint::ZERO);
441        assert_eq!(Uint::<0, 0>::from_le_bytes([]), Uint::ZERO);
442        assert_eq!(
443            Uint::<12, 1>::from_be_bytes([0x01, 0x23]),
444            Uint::from(0x0123)
445        );
446        assert_eq!(
447            Uint::<12, 1>::from_le_bytes([0x23, 0x01]),
448            Uint::from(0x0123)
449        );
450        assert_eq!(
451            Uint::<16, 1>::from_be_bytes([0x12, 0x34]),
452            Uint::from(0x1234)
453        );
454        assert_eq!(
455            Uint::<16, 1>::from_le_bytes([0x34, 0x12]),
456            Uint::from(0x1234)
457        );
458        assert_eq!(Uint::from_be_bytes(BE), N);
459        assert_eq!(Uint::from_le_bytes(LE), N);
460        assert_eq!(Uint::from_be_bytes(KBE), K);
461        assert_eq!(Uint::from_le_bytes(KLE), K);
462    }
463
464    #[test]
465    fn test_to_bytes() {
466        assert_eq!(Uint::<0, 0>::ZERO.to_le_bytes(), [0_u8; 0]);
467        assert_eq!(Uint::<0, 0>::ZERO.to_be_bytes(), [0_u8; 0]);
468        assert_eq!(Uint::<12, 1>::from(0x0123_u64).to_le_bytes(), [0x23, 0x01]);
469        assert_eq!(Uint::<12, 1>::from(0x0123_u64).to_be_bytes(), [0x01, 0x23]);
470        assert_eq!(Uint::<16, 1>::from(0x1234_u64).to_le_bytes(), [0x34, 0x12]);
471        assert_eq!(Uint::<16, 1>::from(0x1234_u64).to_be_bytes(), [0x12, 0x34]);
472        assert_eq!(K.to_be_bytes(), KBE);
473        assert_eq!(K.to_le_bytes(), KLE);
474    }
475
476    #[test]
477    fn test_bytes_roundtrip() {
478        const_for!(BITS in SIZES {
479            const LIMBS: usize = nlimbs(BITS);
480            const BYTES: usize = nbytes(BITS);
481            proptest!(|(value: Uint<BITS, LIMBS>)| {
482                assert_eq!(value, Uint::try_from_le_slice(&value.as_le_bytes()).unwrap());
483                assert_eq!(value, Uint::try_from_le_slice(&value.as_le_bytes_trimmed()).unwrap());
484                assert_eq!(value, Uint::try_from_be_slice(&value.to_be_bytes_trimmed_vec()).unwrap());
485                assert_eq!(value, Uint::try_from_le_slice(&value.to_le_bytes_trimmed_vec()).unwrap());
486                assert_eq!(value, Uint::from_be_bytes(value.to_be_bytes::<BYTES>()));
487                assert_eq!(value, Uint::from_le_bytes(value.to_le_bytes::<BYTES>()));
488            });
489        });
490    }
491}