Skip to main content

fixed_bigint/fixeduint/
from_byte_slice_impl.rs

1//! `const_num_traits::FromByteSlice` for FixedUInt.
2//!
3//! Parses a big/little-endian byte slice of *arbitrary* length up to
4//! `Self::BYTE_WIDTH`, zero-extending shorter input and rejecting
5//! wider input with `ByteSliceErrorKind::Overflow`. Empty input is
6//! `ByteSliceErrorKind::Empty` per upstream — a missing buffer
7//! shouldn't masquerade as zero.
8//!
9//! This is the additive first hop of the byte-trait alignment across
10//! `fixed-bigint` / `rsa/new_reduce` / `ed25519_heapless`. Enables
11//! downstream to drop the `<T as FromBytes>::Bytes: Default +
12//! AsMut<[u8]>` padding-and-dance idiom and bind a single
13//! `T: FromByteSlice` supertrait instead.
14
15use super::{FixedUInt, MachineWord};
16use const_num_traits::{ByteSliceError, ByteSliceErrorKind, FromByteSlice, Personality};
17
18impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
19    /// Length-check for the `FromByteSlice` methods: empty → `Empty`,
20    /// wider-than-`BYTE_WIDTH` → `Overflow`. Kept out-of-line so both
21    /// endianness bodies can share the same failure paths.
22    #[inline]
23    fn check_slice_len(len: usize) -> Result<(), ByteSliceError> {
24        if len == 0 {
25            return Err(ByteSliceError {
26                kind: ByteSliceErrorKind::Empty,
27            });
28        }
29        if len > Self::BYTE_WIDTH {
30            return Err(ByteSliceError {
31                kind: ByteSliceErrorKind::Overflow,
32            });
33        }
34        Ok(())
35    }
36}
37
38impl<T: MachineWord, const N: usize, P: Personality> FromByteSlice for FixedUInt<T, N, P> {
39    fn from_be_slice(bytes: &[u8]) -> Result<Self, ByteSliceError> {
40        Self::check_slice_len(bytes.len())?;
41        // `from_be_bytes` already zero-extends shorter input (BE
42        // reads from the trailing window and initialises the array
43        // to zero).
44        Ok(Self::from_be_bytes(bytes))
45    }
46
47    fn from_le_slice(bytes: &[u8]) -> Result<Self, ByteSliceError> {
48        Self::check_slice_len(bytes.len())?;
49        Ok(Self::from_le_bytes(bytes))
50    }
51}
52
53// No `&FixedUInt: FromByteSlice` mirror — an associated fn returning
54// `Self = &FixedUInt` has no storage source. Consumers who want to
55// parse into an owned `FixedUInt` bind on `FixedUInt: FromByteSlice`;
56// no reference-projection bound is needed on the read side.
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    type U16 = FixedUInt<u8, 2>;
63    type U32 = FixedUInt<u8, 4>;
64
65    #[test]
66    fn from_be_slice_exact() {
67        assert_eq!(U16::from_be_slice(&[0x12, 0x34]), Ok(U16::from(0x1234u16)));
68    }
69
70    #[test]
71    fn from_be_slice_short_zero_extends() {
72        // Two-byte target reading one byte: value is 0x00_12.
73        assert_eq!(U16::from_be_slice(&[0x12]), Ok(U16::from(0x12u8)));
74    }
75
76    #[test]
77    fn from_be_slice_overflow() {
78        assert!(matches!(
79            U16::from_be_slice(&[1, 2, 3]),
80            Err(ByteSliceError {
81                kind: ByteSliceErrorKind::Overflow
82            })
83        ));
84    }
85
86    #[test]
87    fn from_be_slice_empty() {
88        assert!(matches!(
89            U16::from_be_slice(&[]),
90            Err(ByteSliceError {
91                kind: ByteSliceErrorKind::Empty
92            })
93        ));
94    }
95
96    #[test]
97    fn from_le_slice_exact() {
98        assert_eq!(
99            U32::from_le_slice(&[1, 2, 3, 4]),
100            Ok(U32::from(0x04030201u32))
101        );
102    }
103
104    #[test]
105    fn from_le_slice_short_zero_extends() {
106        // Four-byte target reading two bytes: value is 0x00_00_02_01.
107        assert_eq!(U32::from_le_slice(&[1, 2]), Ok(U32::from(0x0201u16)));
108    }
109
110    #[test]
111    fn from_le_slice_overflow() {
112        assert!(matches!(
113            U32::from_le_slice(&[1, 2, 3, 4, 5]),
114            Err(ByteSliceError {
115                kind: ByteSliceErrorKind::Overflow
116            })
117        ));
118    }
119
120    #[test]
121    fn from_le_slice_empty() {
122        assert!(matches!(
123            U32::from_le_slice(&[]),
124            Err(ByteSliceError {
125                kind: ByteSliceErrorKind::Empty
126            })
127        ));
128    }
129
130    #[test]
131    fn le_be_roundtrip_matches_slice_readers() {
132        // Cross-check: FromByteSlice::from_*_slice for exact-width
133        // input agrees with the inherent from_*_bytes.
134        let bytes = [0x12u8, 0x34, 0x56, 0x78];
135        assert_eq!(
136            U32::from_be_slice(&bytes).unwrap(),
137            U32::from_be_bytes(&bytes)
138        );
139        assert_eq!(
140            U32::from_le_slice(&bytes).unwrap(),
141            U32::from_le_bytes(&bytes)
142        );
143    }
144}