Skip to main content

urna_format/
bytes.rs

1//! Little-endian field readers that return a typed error instead of
2//! panicking. Every on-disk decoder used to spell `u32::from_le_bytes(
3//! b[..4].try_into().unwrap())`; the slice was always pre-checked, but a
4//! runtime that opens third-party files should not carry an `unwrap` on
5//! its parse path at all (`clippy::unwrap_used` is denied workspace-wide).
6//! A wrong length here is a decoder bug surfaced as `UnexpectedEof`.
7
8use crate::error::UrnaError;
9
10/// Read a `u32` from exactly 4 little-endian bytes.
11#[inline]
12pub fn le_u32(b: &[u8]) -> crate::Result<u32> {
13    <[u8; 4]>::try_from(b)
14        .map(u32::from_le_bytes)
15        .map_err(|_| UrnaError::UnexpectedEof)
16}
17
18/// Read a `u64` from exactly 8 little-endian bytes.
19#[inline]
20pub fn le_u64(b: &[u8]) -> crate::Result<u64> {
21    <[u8; 8]>::try_from(b)
22        .map(u64::from_le_bytes)
23        .map_err(|_| UrnaError::UnexpectedEof)
24}
25
26/// Read an `f32` from exactly 4 little-endian bytes (bit pattern only; the
27/// caller decides whether NaN/Inf are acceptable).
28#[inline]
29pub fn le_f32(b: &[u8]) -> crate::Result<f32> {
30    <[u8; 4]>::try_from(b)
31        .map(f32::from_le_bytes)
32        .map_err(|_| UrnaError::UnexpectedEof)
33}
34
35/// Copy exactly 32 bytes (a sha256 digest) into an array.
36#[inline]
37pub fn array32(b: &[u8]) -> crate::Result<[u8; 32]> {
38    <[u8; 32]>::try_from(b).map_err(|_| UrnaError::UnexpectedEof)
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn exact_lengths_decode_and_wrong_lengths_are_typed_errors() {
47        assert_eq!(le_u32(&[1, 0, 0, 0]).unwrap(), 1);
48        assert_eq!(le_u64(&[2, 0, 0, 0, 0, 0, 0, 0]).unwrap(), 2);
49        assert_eq!(le_f32(&1.5f32.to_le_bytes()).unwrap(), 1.5);
50        assert_eq!(array32(&[7u8; 32]).unwrap(), [7u8; 32]);
51        assert!(matches!(le_u32(&[1, 0, 0]), Err(UrnaError::UnexpectedEof)));
52        assert!(matches!(le_u64(&[0; 9]), Err(UrnaError::UnexpectedEof)));
53        assert!(matches!(array32(&[0; 31]), Err(UrnaError::UnexpectedEof)));
54    }
55}