use std::ptr::read_unaligned;
use crate::{
constants::{
CHUNK_SIZE_1024, LEN_TAG_1024, LEN_TAG_MASK, LEN_TAG_SHIFT, LEN_TAG_U8, LEN_TAG_U16,
LEN_TAG_U32, TYPE_F32_RAW, TYPE_F64_RAW, TYPE_MASK,
},
error::{Error, Result},
params::unpack_params,
};
pub const MAX_HEADER_LEN: usize = 7;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParsedHeader {
pub type_byte: u8,
pub count: usize,
pub len_tag: u8,
pub params: Option<(u8, u8, u8)>,
pub cursor: usize,
}
#[inline(always)]
pub const fn count_bytes(count: usize) -> usize {
if count == CHUNK_SIZE_1024 {
0
} else if count <= u8::MAX as usize {
1
} else if count <= u16::MAX as usize {
2
} else {
4
}
}
#[inline(always)]
pub const fn header_len(count: usize) -> usize {
1 + count_bytes(count) + 2
}
#[inline(always)]
pub const fn raw_header_len(count: usize) -> usize {
1 + count_bytes(count)
}
#[inline(always)]
pub fn write_header(type_byte: u8, count: usize, params: Option<u16>, dst: &mut Vec<u8>) {
debug_assert!(count <= u32::MAX as usize, "count exceeds u32::MAX");
let mut buf = [0u8; MAX_HEADER_LEN];
let mut len = 1;
let (len_tag, count_len) = if count == CHUNK_SIZE_1024 {
(LEN_TAG_1024, 0)
} else if count <= u8::MAX as usize {
buf[1] = count as u8;
(LEN_TAG_U8, 1)
} else if count <= u16::MAX as usize {
buf[1..3].copy_from_slice(&(count as u16).to_le_bytes());
(LEN_TAG_U16, 2)
} else {
buf[1..5].copy_from_slice(&(count as u32).to_le_bytes());
(LEN_TAG_U32, 4)
};
buf[0] = (type_byte & TYPE_MASK) | (len_tag << LEN_TAG_SHIFT);
len += count_len;
if let Some(p) = params {
buf[len..len + 2].copy_from_slice(&p.to_le_bytes());
len += 2;
}
dst.extend_from_slice(&buf[..len]);
}
#[inline(always)]
pub fn read_header(src: &[u8]) -> Result<ParsedHeader> {
if src.is_empty() {
return Err(Error::UnexpectedEof {
needed: 1,
available: 0,
});
}
let desc_byte = src[0];
let type_byte = desc_byte & TYPE_MASK;
let len_tag = (desc_byte >> LEN_TAG_SHIFT) & LEN_TAG_MASK;
let mut cursor = 1;
let count = match len_tag {
LEN_TAG_1024 => CHUNK_SIZE_1024,
LEN_TAG_U8 => {
if src.len() < cursor + 1 {
return Err(Error::UnexpectedEof {
needed: cursor + 1,
available: src.len(),
});
}
let c = src[cursor] as usize;
cursor += 1;
c
}
LEN_TAG_U16 => {
if src.len() < cursor + 2 {
return Err(Error::UnexpectedEof {
needed: cursor + 2,
available: src.len(),
});
}
let c =
unsafe { u16::from_le(read_unaligned(src.as_ptr().add(cursor).cast::<u16>())) } as usize;
cursor += 2;
c
}
LEN_TAG_U32 => {
if src.len() < cursor + 4 {
return Err(Error::UnexpectedEof {
needed: cursor + 4,
available: src.len(),
});
}
let c =
unsafe { u32::from_le(read_unaligned(src.as_ptr().add(cursor).cast::<u32>())) } as usize;
cursor += 4;
c
}
_ => unsafe { core::hint::unreachable_unchecked() },
};
let is_raw = type_byte == TYPE_F64_RAW || type_byte == TYPE_F32_RAW;
if is_raw || count == 0 {
return Ok(ParsedHeader {
type_byte,
count,
len_tag,
params: None,
cursor,
});
}
if src.len() < cursor + 2 {
return Err(Error::UnexpectedEof {
needed: cursor + 2,
available: src.len(),
});
}
let raw_params = unsafe { u16::from_le(read_unaligned(src.as_ptr().add(cursor).cast::<u16>())) };
cursor += 2;
let (exp, fac, bit_width) = unpack_params(raw_params);
Ok(ParsedHeader {
type_byte,
count,
len_tag,
params: Some((exp, fac, bit_width)),
cursor,
})
}