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.swap_bytes();
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, and must be at most
220    /// [`Self::BYTES`] long.
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, and must be at most
238    /// [`Self::BYTES`] long.
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            let (limb, byte) = (i / 8, i % 8);
266            limbs[limb] += (bytes[c] as u64) << (byte * 8);
267            i += 1;
268        }
269        if Self::LIMBS > 0 && limbs[Self::LIMBS - 1] > Self::MASK {
270            return None;
271        }
272        Some(Self::from_limbs(limbs))
273    }
274
275    /// Converts a little-endian byte array of size exactly
276    /// [`Self::BYTES`] to [`Uint`].
277    ///
278    /// # Panics
279    ///
280    /// Panics if the generic parameter `BYTES` is not exactly [`Self::BYTES`].
281    /// Ideally this would be a compile time error, but this is blocked by
282    /// Rust issue [#60551].
283    ///
284    /// [#60551]: https://github.com/rust-lang/rust/issues/60551
285    ///
286    /// Panics if the value is too large for the bit-size of the Uint.
287    #[must_use]
288    #[track_caller]
289    #[inline]
290    pub const fn from_le_bytes<const BYTES: usize>(bytes: [u8; BYTES]) -> Self {
291        // TODO: Use a `const {}` block for this assertion
292        assert!(BYTES == Self::BYTES, "BYTES must be equal to Self::BYTES");
293        Self::from_le_slice(&bytes)
294    }
295
296    /// Creates a new integer from a little endian slice of bytes.
297    ///
298    /// The slice is interpreted as a little endian number, and must be at most
299    /// [`Self::BYTES`] long.
300    ///
301    /// # Panics
302    ///
303    /// Panics if the value is larger than fits the [`Uint`].
304    #[must_use]
305    #[track_caller]
306    #[inline]
307    pub const fn from_le_slice(bytes: &[u8]) -> Self {
308        match Self::try_from_le_slice(bytes) {
309            Some(value) => value,
310            None => panic!("Value too large for Uint"),
311        }
312    }
313
314    /// Creates a new integer from a little endian slice of bytes.
315    ///
316    /// The slice is interpreted as a little endian number, and must be at most
317    /// [`Self::BYTES`] long.
318    ///
319    /// Returns [`None`] if the value is larger than fits the [`Uint`].
320    #[must_use]
321    #[inline]
322    pub const fn try_from_le_slice(bytes: &[u8]) -> Option<Self> {
323        if bytes.len() > Self::BYTES {
324            return None;
325        }
326
327        if Self::BYTES % 8 == 0 && bytes.len() == Self::BYTES {
328            // Optimized implementation for full-limb types.
329            let mut limbs = [0; LIMBS];
330            let mut i = 0;
331            while i < LIMBS {
332                limbs[i] = u64::from_le_bytes(unsafe { *bytes.as_ptr().add(i * 8).cast() });
333                i += 1;
334            }
335            return Some(Self::from_limbs(limbs));
336        }
337
338        let mut limbs = [0; LIMBS];
339        let mut i = 0;
340        while i < bytes.len() {
341            let (limb, byte) = (i / 8, i % 8);
342            limbs[limb] += (bytes[i] as u64) << (byte * 8);
343            i += 1;
344        }
345        if Self::LIMBS > 0 && limbs[Self::LIMBS - 1] > Self::MASK {
346            return None;
347        }
348        Some(Self::from_limbs(limbs))
349    }
350
351    /// Writes the little-endian representation of the [`Uint`] to the given
352    /// buffer. The buffer must be large enough to hold [`Self::BYTES`] bytes.
353    ///
354    /// # Panics
355    ///
356    /// Panics if the buffer is not large enough to hold [`Self::BYTES`] bytes.
357    ///
358    /// # Returns
359    ///
360    /// The number of bytes written to the buffer (always equal to
361    /// [`Self::BYTES`], but often useful to make explicit for encoders).
362    #[inline]
363    pub fn copy_le_bytes_to(&self, buf: &mut [u8]) -> usize {
364        // This is debug only. Release panics occur later in copy_from_slice
365        debug_assert!(
366            buf.len() >= Self::BYTES,
367            "Buffer is too small to hold the bytes of the Uint"
368        );
369
370        #[cfg(target_endian = "little")]
371        buf[..Self::BYTES].copy_from_slice(self.as_le_slice());
372
373        #[cfg(target_endian = "big")]
374        {
375            let chunks = buf[..Self::BYTES].chunks_mut(8);
376
377            self.limbs.iter().zip(chunks).for_each(|(&limb, chunk)| {
378                let le = limb.to_le_bytes();
379                chunk.copy_from_slice(&le[..chunk.len()]);
380            });
381        }
382
383        Self::BYTES
384    }
385
386    /// Writes the little-endian representation of the [`Uint`] to the given
387    /// buffer. The buffer must be large enough to hold [`Self::BYTES`] bytes.
388    ///
389    /// # Returns
390    ///
391    /// [`None`], if the buffer is not large enough to hold [`Self::BYTES`]
392    /// bytes, and does not modify the buffer.
393    ///
394    /// [`Some`] with the number of bytes written to the buffer (always
395    /// equal to [`Self::BYTES`], but often useful to make explicit for
396    /// encoders).
397    #[inline]
398    pub fn checked_copy_le_bytes_to(&self, buf: &mut [u8]) -> Option<usize> {
399        if buf.len() < Self::BYTES {
400            return None;
401        }
402
403        Some(self.copy_le_bytes_to(buf))
404    }
405
406    /// Writes the big-endian representation of the [`Uint`] to the given
407    /// buffer. The buffer must be large enough to hold [`Self::BYTES`] bytes.
408    ///
409    /// # Panics
410    ///
411    /// Panics if the buffer is not large enough to hold [`Self::BYTES`] bytes.
412    ///
413    /// # Returns
414    ///
415    /// The number of bytes written to the buffer (always equal to
416    /// [`Self::BYTES`], but often useful to make explicit for encoders).
417    #[inline]
418    pub fn copy_be_bytes_to(&self, buf: &mut [u8]) -> usize {
419        // This is debug only. Release panics occur later in copy_from_slice
420        debug_assert!(
421            buf.len() >= Self::BYTES,
422            "Buffer is too small to hold the bytes of the Uint"
423        );
424
425        // start from the end of the slice
426        let chunks = buf[..Self::BYTES].rchunks_mut(8);
427
428        self.limbs.iter().zip(chunks).for_each(|(&limb, chunk)| {
429            let be = limb.to_be_bytes();
430            let copy_from = 8 - chunk.len();
431            chunk.copy_from_slice(&be[copy_from..]);
432        });
433
434        Self::BYTES
435    }
436
437    /// Writes the big-endian representation of the [`Uint`] to the given
438    /// buffer. The buffer must be large enough to hold [`Self::BYTES`] bytes.
439    ///
440    /// # Returns
441    ///
442    /// [`None`], if the buffer is not large enough to hold [`Self::BYTES`]
443    /// bytes, and does not modify the buffer.
444    ///
445    /// [`Some`] with the number of bytes written to the buffer (always
446    /// equal to [`Self::BYTES`], but often useful to make explicit for
447    /// encoders).
448    #[inline]
449    pub fn checked_copy_be_bytes_to(&self, buf: &mut [u8]) -> Option<usize> {
450        if buf.len() < Self::BYTES {
451            return None;
452        }
453
454        Some(self.copy_be_bytes_to(buf))
455    }
456}
457
458/// Number of bytes required to represent the given number of bits.
459///
460/// This needs to be public because it is used in the `Uint` type,
461/// specifically in the [`to_be_bytes()`][Uint::to_be_bytes] and related
462/// functions.
463#[inline]
464#[must_use]
465pub const fn nbytes(bits: usize) -> usize {
466    (bits + 7) / 8
467}
468
469#[cfg(test)]
470mod tests {
471    use super::*;
472    use crate::{const_for, nlimbs};
473    use proptest::proptest;
474
475    const N: Uint<128, 2> =
476        Uint::from_limbs([0x7890_1234_5678_9012_u64, 0x1234_5678_9012_3456_u64]);
477    const BE: [u8; 16] = [
478        0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90,
479        0x12,
480    ];
481    const LE: [u8; 16] = [
482        0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34,
483        0x12,
484    ];
485
486    const K: Uint<72, 2> = Uint::from_limbs([0x3456_7890_1234_5678_u64, 0x12_u64]);
487    const KBE: [u8; 9] = [0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78];
488    const KLE: [u8; 9] = [0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12];
489
490    #[test]
491    const fn const_from_to_bytes() {
492        const NL: [u64; 2] = N.limbs;
493        const KL: [u64; 2] = K.limbs;
494        assert!(matches!(Uint::<128, 2>::from_be_bytes(BE).limbs, NL));
495        assert!(matches!(Uint::<128, 2>::from_le_bytes(LE).limbs, NL));
496        assert!(matches!(N.to_be_bytes::<{ BE.len() }>(), BE));
497        assert!(matches!(N.to_le_bytes::<{ LE.len() }>(), LE));
498
499        assert!(matches!(Uint::<72, 2>::from_be_bytes(KBE).limbs, KL));
500        assert!(matches!(Uint::<72, 2>::from_le_bytes(KLE).limbs, KL));
501        assert!(matches!(K.to_be_bytes::<{ KBE.len() }>(), KBE));
502        assert!(matches!(K.to_le_bytes::<{ KLE.len() }>(), KLE));
503
504        assert!(matches!(Uint::<0, 0>::ZERO.to_be_bytes::<0>(), []));
505        assert!(matches!(Uint::<1, 1>::ZERO.to_be_bytes::<1>(), [0]));
506        assert!(matches!(
507            Uint::<1, 1>::from_limbs([1]).to_be_bytes::<1>(),
508            [1]
509        ));
510        assert!(matches!(
511            Uint::<16, 1>::from_limbs([0x1234]).to_be_bytes::<2>(),
512            [0x12, 0x34]
513        ));
514
515        assert!(matches!(Uint::<0, 0>::ZERO.to_be_bytes::<0>(), []));
516        assert!(matches!(Uint::<0, 0>::ZERO.to_le_bytes::<0>(), []));
517        assert!(matches!(Uint::<1, 1>::ZERO.to_be_bytes::<1>(), [0]));
518        assert!(matches!(Uint::<1, 1>::ZERO.to_le_bytes::<1>(), [0]));
519        assert!(matches!(
520            Uint::<1, 1>::from_limbs([1]).to_be_bytes::<1>(),
521            [1]
522        ));
523        assert!(matches!(
524            Uint::<1, 1>::from_limbs([1]).to_le_bytes::<1>(),
525            [1]
526        ));
527        assert!(matches!(
528            Uint::<16, 1>::from_limbs([0x1234]).to_be_bytes::<2>(),
529            [0x12, 0x34]
530        ));
531        assert!(matches!(
532            Uint::<16, 1>::from_limbs([0x1234]).to_le_bytes::<2>(),
533            [0x34, 0x12]
534        ));
535
536        assert!(matches!(
537            Uint::<63, 1>::from_limbs([0x010203]).to_be_bytes::<8>(),
538            [0, 0, 0, 0, 0, 1, 2, 3]
539        ));
540        assert!(matches!(
541            Uint::<63, 1>::from_limbs([0x010203]).to_le_bytes::<8>(),
542            [3, 2, 1, 0, 0, 0, 0, 0]
543        ));
544    }
545
546    #[test]
547    fn test_from_bytes() {
548        assert_eq!(Uint::<0, 0>::from_be_bytes([]), Uint::ZERO);
549        assert_eq!(Uint::<0, 0>::from_le_bytes([]), Uint::ZERO);
550        assert_eq!(
551            Uint::<12, 1>::from_be_bytes([0x01, 0x23]),
552            Uint::from(0x0123)
553        );
554        assert_eq!(
555            Uint::<12, 1>::from_le_bytes([0x23, 0x01]),
556            Uint::from(0x0123)
557        );
558        assert_eq!(
559            Uint::<16, 1>::from_be_bytes([0x12, 0x34]),
560            Uint::from(0x1234)
561        );
562        assert_eq!(
563            Uint::<16, 1>::from_le_bytes([0x34, 0x12]),
564            Uint::from(0x1234)
565        );
566
567        assert_eq!(Uint::from_be_bytes(BE), N);
568        assert_eq!(Uint::from_le_bytes(LE), N);
569        assert_eq!(Uint::from_be_bytes(KBE), K);
570        assert_eq!(Uint::from_le_bytes(KLE), K);
571
572        assert_eq!(Uint::<128, 2>::try_from_be_slice(&BE), Some(N));
573        assert_eq!(
574            Uint::<128, 2>::try_from_be_slice(&[&BE[..], &[0xff][..]].concat()),
575            None
576        );
577        assert_eq!(Uint::<128, 2>::try_from_le_slice(&LE), Some(N));
578        assert_eq!(
579            Uint::<128, 2>::try_from_le_slice(&[&LE[..], &[0xff]].concat()),
580            None
581        );
582        assert_eq!(Uint::<72, 2>::try_from_be_slice(&KBE), Some(K));
583        assert_eq!(
584            Uint::<72, 2>::try_from_be_slice(&[&KBE[..], &[0xff][..]].concat()),
585            None
586        );
587        assert_eq!(Uint::<72, 2>::try_from_le_slice(&KLE), Some(K));
588        assert_eq!(
589            Uint::<72, 2>::try_from_le_slice(&[&KLE[..], &[0xff]].concat()),
590            None
591        );
592    }
593
594    #[test]
595    fn test_to_bytes() {
596        assert_eq!(Uint::<0, 0>::ZERO.to_le_bytes(), [0_u8; 0]);
597        assert_eq!(Uint::<0, 0>::ZERO.to_be_bytes(), [0_u8; 0]);
598        assert_eq!(Uint::<12, 1>::from(0x0123_u64).to_le_bytes(), [0x23, 0x01]);
599        assert_eq!(Uint::<12, 1>::from(0x0123_u64).to_be_bytes(), [0x01, 0x23]);
600        assert_eq!(Uint::<16, 1>::from(0x1234_u64).to_le_bytes(), [0x34, 0x12]);
601        assert_eq!(Uint::<16, 1>::from(0x1234_u64).to_be_bytes(), [0x12, 0x34]);
602        assert_eq!(K.to_be_bytes(), KBE);
603        assert_eq!(K.to_le_bytes(), KLE);
604    }
605
606    #[test]
607    fn test_bytes_roundtrip() {
608        const_for!(BITS in SIZES {
609            const LIMBS: usize = nlimbs(BITS);
610            const BYTES: usize = nbytes(BITS);
611            proptest!(|(value: Uint<BITS, LIMBS>)| {
612                assert_eq!(value, Uint::try_from_le_slice(&value.as_le_bytes()).unwrap());
613                assert_eq!(value, Uint::try_from_le_slice(&value.as_le_bytes_trimmed()).unwrap());
614                assert_eq!(value, Uint::try_from_be_slice(&value.to_be_bytes_trimmed_vec()).unwrap());
615                assert_eq!(value, Uint::try_from_le_slice(&value.to_le_bytes_trimmed_vec()).unwrap());
616                assert_eq!(value, Uint::from_be_bytes(value.to_be_bytes::<BYTES>()));
617                assert_eq!(value, Uint::from_le_bytes(value.to_le_bytes::<BYTES>()));
618            });
619        });
620    }
621
622    #[test]
623    fn copy_to() {
624        const_for!(BITS in SIZES {
625            const LIMBS: usize = nlimbs(BITS);
626            const BYTES: usize = nbytes(BITS);
627            proptest!(|(value: Uint<BITS, LIMBS>)|{
628                let mut buf = [0; BYTES];
629                value.copy_le_bytes_to(&mut buf);
630                assert_eq!(buf, value.to_le_bytes::<BYTES>());
631                assert_eq!(value, Uint::try_from_le_slice(&buf).unwrap());
632
633                let mut buf = [0; BYTES];
634                value.copy_be_bytes_to(&mut buf);
635                assert_eq!(buf, value.to_be_bytes::<BYTES>());
636                assert_eq!(value, Uint::try_from_be_slice(&buf).unwrap());
637            });
638        });
639    }
640
641    #[test]
642    fn checked_copy_to() {
643        const_for!(BITS in SIZES {
644            const LIMBS: usize = nlimbs(BITS);
645            const BYTES: usize = nbytes(BITS);
646            proptest!(|(value: Uint<BITS, LIMBS>)|{
647                if BYTES != 0 {
648                    let mut buf = [0; BYTES];
649                    let too_short = buf.len() - 1;
650
651                    assert_eq!(value.checked_copy_le_bytes_to(&mut buf[..too_short]), None);
652                    assert_eq!(buf, [0; BYTES], "buffer was modified");
653
654                    assert_eq!(value.checked_copy_be_bytes_to(&mut buf[..too_short]), None);
655                    assert_eq!(buf, [0; BYTES], "buffer was modified");
656                }
657            });
658        });
659    }
660}