use core::mem::size_of;
use crate::error::{Incomplete, InvalidWidth};
pub fn take(buf: &[u8], n: usize) -> Result<(&[u8], &[u8]), Incomplete> {
if buf.len() < n {
Err(Incomplete {
needed: n,
available: buf.len(),
})
} else {
Ok(buf.split_at(n))
}
}
pub fn read_u8(buf: &[u8]) -> Result<(u8, &[u8]), Incomplete> {
let (b, rest) = take(buf, 1)?;
Ok((b[0], rest))
}
pub fn read_u16_be(buf: &[u8]) -> Result<(u16, &[u8]), Incomplete> {
let (b, rest) = take(buf, 2)?;
Ok((u16::from_be_bytes([b[0], b[1]]), rest))
}
pub fn read_u32_be(buf: &[u8]) -> Result<(u32, &[u8]), Incomplete> {
let (b, rest) = take(buf, 4)?;
Ok((u32::from_be_bytes([b[0], b[1], b[2], b[3]]), rest))
}
pub fn read_u64_be(buf: &[u8]) -> Result<(u64, &[u8]), Incomplete> {
let (b, rest) = take(buf, 8)?;
Ok((
u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
rest,
))
}
pub fn read_u128_be(buf: &[u8]) -> Result<(u128, &[u8]), Incomplete> {
let (arr, rest) = read_array::<16>(buf)?;
Ok((u128::from_be_bytes(arr), rest))
}
pub fn read_array<const N: usize>(buf: &[u8]) -> Result<([u8; N], &[u8]), Incomplete> {
let (b, rest) = take(buf, N)?;
let mut arr = [0u8; N];
arr.copy_from_slice(b);
Ok((arr, rest))
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReadUintError {
Incomplete(Incomplete),
InvalidWidth(InvalidWidth),
}
impl From<Incomplete> for ReadUintError {
fn from(e: Incomplete) -> Self {
ReadUintError::Incomplete(e)
}
}
impl From<InvalidWidth> for ReadUintError {
fn from(e: InvalidWidth) -> Self {
ReadUintError::InvalidWidth(e)
}
}
impl core::fmt::Display for ReadUintError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ReadUintError::Incomplete(e) => e.fmt(f),
ReadUintError::InvalidWidth(e) => e.fmt(f),
}
}
}
impl core::error::Error for ReadUintError {}
pub fn read_be_uint(buf: &[u8], n: usize) -> Result<(u128, &[u8]), ReadUintError> {
if n > 16 {
return Err(InvalidWidth { max: 16, got: n }.into());
}
let (b, rest) = take(buf, n)?;
let mut acc: u128 = 0;
for &byte in b {
acc = (acc << 8) | u128::from(byte);
}
Ok((acc, rest))
}
mod sealed {
pub trait Sealed {}
}
pub trait BeUint: sealed::Sealed + Sized {
const BYTES: usize;
#[doc(hidden)]
fn from_u128(v: u128) -> Self;
}
macro_rules! impl_be_uint {
($($t:ty),*) => {$(
impl sealed::Sealed for $t {}
impl BeUint for $t {
const BYTES: usize = size_of::<$t>();
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
fn from_u128(v: u128) -> Self {
v as $t
}
}
)*};
}
impl_be_uint!(u8, u16, u32, u64, u128);
pub fn read_be_uint_into<T: BeUint>(buf: &[u8], n: usize) -> Result<(T, &[u8]), ReadUintError> {
if n > T::BYTES {
return Err(InvalidWidth {
max: T::BYTES,
got: n,
}
.into());
}
let (v, rest) = read_be_uint(buf, n)?;
Ok((T::from_u128(v), rest))
}
#[inline]
pub fn ensure_len(buf: &[u8], needed: usize) -> Result<(), Incomplete> {
if buf.len() < needed {
Err(Incomplete {
needed,
available: buf.len(),
})
} else {
Ok(())
}
}
#[must_use]
pub fn read_optional_array<const N: usize>(buf: &[u8]) -> (Option<[u8; N]>, &[u8]) {
match read_array::<N>(buf) {
Ok((arr, rest)) => (Some(arr), rest),
Err(_) => (None, buf),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn take_splits_and_returns_remainder() {
let (head, tail) = take(&[1, 2, 3, 4], 2).unwrap();
assert_eq!(head, &[1, 2]);
assert_eq!(tail, &[3, 4]);
}
#[test]
fn take_past_end_is_incomplete() {
let buf = [1, 2, 3];
assert_eq!(
take(&buf, 4),
Err(Incomplete {
needed: 4,
available: 3
})
);
}
#[test]
fn read_u16_be_reads_big_endian_and_remainder() {
let (v, rest) = read_u16_be(&[0x12, 0x34, 0x56]).unwrap();
assert_eq!(v, 0x1234);
assert_eq!(rest, &[0x56]);
}
#[test]
fn read_u32_be_exact_leaves_empty_remainder() {
let (v, rest) = read_u32_be(&[0xDE, 0xAD, 0xBE, 0xEF]).unwrap();
assert_eq!(v, 0xDEAD_BEEF);
assert!(rest.is_empty());
}
#[test]
fn read_helpers_one_byte_short_are_incomplete() {
assert_eq!(
read_u8(&[]),
Err(Incomplete {
needed: 1,
available: 0
})
);
assert_eq!(
read_u16_be(&[0]),
Err(Incomplete {
needed: 2,
available: 1
})
);
assert_eq!(
read_u32_be(&[0; 3]),
Err(Incomplete {
needed: 4,
available: 3
})
);
assert_eq!(
read_u64_be(&[0; 7]),
Err(Incomplete {
needed: 8,
available: 7
})
);
}
#[test]
fn read_array_reads_fixed_width() {
let (arr, rest) = read_array::<3>(&[1, 2, 3, 4]).unwrap();
assert_eq!(arr, [1, 2, 3]);
assert_eq!(rest, &[4]);
assert_eq!(
read_array::<3>(&[1, 2]),
Err(Incomplete {
needed: 3,
available: 2
})
);
}
#[test]
fn read_be_uint_reads_low_n_bytes() {
let (v, rest) = read_be_uint(&[0x01, 0x02, 0x03], 2).unwrap();
assert_eq!(v, 0x0102);
assert_eq!(rest, &[0x03]);
assert_eq!(
read_be_uint(&[0x00], 2),
Err(ReadUintError::Incomplete(Incomplete {
needed: 2,
available: 1
}))
);
}
#[test]
fn read_be_uint_hostile_width_is_data_error_not_panic() {
let buf = [0u8; 300];
assert_eq!(
read_be_uint(&buf, 255),
Err(ReadUintError::InvalidWidth(InvalidWidth {
max: 16,
got: 255
}))
);
assert_eq!(
read_be_uint(&buf, 17),
Err(ReadUintError::InvalidWidth(InvalidWidth {
max: 16,
got: 17
}))
);
}
#[test]
fn read_be_uint_zero_width_reads_nothing() {
let buf = [0xAA, 0xBB];
let (v, rest) = read_be_uint(&buf, 0).unwrap();
assert_eq!(v, 0);
assert_eq!(rest, &buf);
}
#[test]
fn read_be_uint_into_returns_target_type() {
let buf = [0x01, 0x02, 0x03, 0xFF];
let (v, rest) = read_be_uint_into::<u32>(&buf, 3).unwrap();
assert_eq!(v, 0x0001_0203_u32);
assert_eq!(rest, &[0xFF]);
}
#[test]
fn read_be_uint_into_rejects_width_wider_than_target() {
let buf = [0u8; 8];
assert_eq!(
read_be_uint_into::<u32>(&buf, 5),
Err(ReadUintError::InvalidWidth(InvalidWidth { max: 4, got: 5 }))
);
}
#[test]
fn read_be_uint_into_full_width_roundtrips() {
let buf = 0xDEAD_BEEF_u32.to_be_bytes();
let (v, rest) = read_be_uint_into::<u32>(&buf, 4).unwrap();
assert_eq!(v, 0xDEAD_BEEF);
assert!(rest.is_empty());
let (b, _) = read_be_uint_into::<u8>(&buf, 1).unwrap();
assert_eq!(b, 0xDE);
}
#[test]
fn read_u128_be_reads_full_width() {
let v = 0x0102_0304_0506_0708_090A_0B0C_0D0E_0F10_u128;
let bytes = v.to_be_bytes();
let mut buf = [0u8; 17];
buf[..16].copy_from_slice(&bytes);
buf[16] = 0xFF;
let (got, rest) = read_u128_be(&buf).unwrap();
assert_eq!(got, v);
assert_eq!(rest, &[0xFF]);
assert_eq!(
read_u128_be(&[0u8; 15]),
Err(Incomplete {
needed: 16,
available: 15
})
);
}
#[test]
fn ensure_len_passes_and_fails_with_counts() {
assert_eq!(ensure_len(&[1, 2, 3], 3), Ok(()));
assert_eq!(ensure_len(&[1, 2, 3], 2), Ok(()));
assert_eq!(
ensure_len(&[1, 2, 3], 4),
Err(Incomplete {
needed: 4,
available: 3
})
);
}
#[test]
fn read_optional_array_present_and_absent() {
let (arr, rest) = read_optional_array::<4>(&[1, 2, 3, 4, 5]);
assert_eq!(arr, Some([1, 2, 3, 4]));
assert_eq!(rest, &[5]);
let (arr, rest) = read_optional_array::<4>(&[]);
assert_eq!(arr, None);
assert!(rest.is_empty());
let (arr, rest) = read_optional_array::<4>(&[1, 2]);
assert_eq!(arr, None);
assert_eq!(rest, &[1, 2]);
}
}