use core::hint::unreachable_unchecked;
use std::{mem::size_of, ptr::read_unaligned};
use crate::error::{Error, Result};
pub use crate::{
constants::{
CHUNK_SIZE, CHUNK_SIZE_1024, LEN_TAG_1024, LEN_TAG_MASK, LEN_TAG_SHIFT, LEN_TAG_U8,
LEN_TAG_U16, LEN_TAG_U32, MAX_TYPE_BYTE, TYPE_F32, TYPE_F32_DEC, TYPE_F32_DEC_DELTA,
TYPE_F32_DELTA, TYPE_F32_RAW, TYPE_F64, TYPE_F64_DEC, TYPE_F64_DEC_DELTA, TYPE_F64_DELTA,
TYPE_F64_RAW, TYPE_MASK,
},
params::AlpParams,
};
const DESC_LEN: usize = 1;
const MAX_COUNT_LEN: usize = size_of::<u32>();
const PARAMS_LEN: usize = size_of::<u16>();
pub const MAX_HEADER_LEN: usize = DESC_LEN + MAX_COUNT_LEN + PARAMS_LEN;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParsedHeader {
pub type_byte: u8,
pub count: usize,
pub len_tag: u8,
pub params: Option<AlpParams>,
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_count(src: &[u8]) -> Result<usize> {
if src.is_empty() {
return Err(Error::UnexpectedEof {
needed: 1,
available: 0,
});
}
let desc_byte = src[0];
let type_byte = desc_byte & TYPE_MASK;
if type_byte == 0 || type_byte > MAX_TYPE_BYTE {
return Err(Error::InvalidHeader);
}
let len_tag = (desc_byte >> LEN_TAG_SHIFT) & LEN_TAG_MASK;
match len_tag {
LEN_TAG_1024 => Ok(CHUNK_SIZE_1024),
LEN_TAG_U8 => {
if src.len() < 2 {
return Err(Error::UnexpectedEof {
needed: 2,
available: src.len(),
});
}
Ok(src[1] as usize)
}
LEN_TAG_U16 => {
if src.len() < 3 {
return Err(Error::UnexpectedEof {
needed: 3,
available: src.len(),
});
}
let c = unsafe { u16::from_le(read_unaligned(src.as_ptr().add(1).cast::<u16>())) } as usize;
Ok(c)
}
LEN_TAG_U32 => {
if src.len() < 5 {
return Err(Error::UnexpectedEof {
needed: 5,
available: src.len(),
});
}
let c = unsafe { u32::from_le(read_unaligned(src.as_ptr().add(1).cast::<u32>())) } as usize;
Ok(c)
}
_ => unsafe { unreachable_unchecked() },
}
}
#[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;
if type_byte == 0 || type_byte > MAX_TYPE_BYTE {
return Err(Error::InvalidHeader);
}
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 { 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 is_dec = type_byte == TYPE_F64_DEC
|| type_byte == TYPE_F32_DEC
|| type_byte == TYPE_F64_DEC_DELTA
|| type_byte == TYPE_F32_DEC_DELTA;
let alp_params = AlpParams::from_packed(raw_params, is_dec);
Ok(ParsedHeader {
type_byte,
count,
len_tag,
params: Some(alp_params),
cursor,
})
}