mod read;
mod write;
use crate::*;
pub const PACKET_SIG: [u8; 8] = [236u8, 37u8, 94u8, 136u8, 236u8, 37u8, 94u8, 136u8];
#[derive(Debug)]
pub struct PacketHeader {
pub size: u64,
pub blocks_len: u64,
pub payload: bool,
pub crc: u32,
}
impl PacketHeader {
pub const SIZE: u64 = (
PACKET_SIG.len()
+ std::mem::size_of::<u64>()
+ std::mem::size_of::<u64>()
+ 1
+ std::mem::size_of::<u32>()
) as u64;
pub(crate) fn from_lengths(blocks_len: u64, payload_len: u64, has_payload: bool) -> Self {
let size = blocks_len + payload_len;
let mut hasher = crc32fast::Hasher::new();
hasher.update(&size.to_le_bytes());
hasher.update(&blocks_len.to_le_bytes());
hasher.update(if has_payload { &[1] } else { &[0] });
let crc = hasher.finalize();
Self {
size,
blocks_len,
payload: has_payload,
crc,
}
}
pub fn new<B: BlockDef, Inner: PayloadInnerDef>(
blocks: &[B],
payload: Option<&Inner>,
ctx: &mut <Inner as PayloadSchema>::Context<'_>,
) -> std::io::Result<Self> {
let blocks_len: u64 = blocks.iter().map(|blk| blk.size()).sum();
let payload_len: u64 = payload
.as_ref()
.map(|payload| Self::payload_size(*payload, ctx))
.unwrap_or(Ok(0))?;
Ok(Self::from_lengths(
blocks_len,
payload_len,
payload.is_some(),
))
}
pub fn payload_size<Inner: PayloadInnerDef>(
payload: &Inner,
ctx: &mut <Inner as PayloadSchema>::Context<'_>,
) -> std::io::Result<u64> {
let payload_header_len: u64 = PayloadHeader::ssize::<Inner>(payload).map(|s| s as u64)?;
let payload_body_len: u64 = payload.size(ctx)?;
Ok(payload_body_len + payload_header_len)
}
pub fn get_pos(buffer: &[u8]) -> Option<usize> {
let mut offset = 0;
while buffer.len() > offset + PACKET_SIG.len() {
if buffer[offset..].starts_with(&PACKET_SIG) {
return Some(offset);
} else {
offset += 1;
continue;
}
}
None
}
pub fn is_not_enought(buffer: &[u8]) -> Option<usize> {
if buffer.len() >= Self::SIZE as usize {
None
} else {
Some(Self::SIZE as usize - buffer.len())
}
}
}
impl StaticSize for PacketHeader {
fn ssize() -> u64 {
PacketHeader::SIZE
}
}
impl CrcU32 for PacketHeader {
fn crc(&self) -> [u8; 4] {
let mut hasher = crc32fast::Hasher::new();
hasher.update(&self.size.to_le_bytes());
hasher.update(&self.blocks_len.to_le_bytes());
hasher.update(if self.payload { &[1] } else { &[0] });
hasher.finalize().to_le_bytes()
}
}
#[cfg(test)]
mod tests {
use super::{PACKET_SIG, PacketHeader};
use crate::{CrcU32, StaticSize, default_payload_context, tests::*};
#[test]
fn packet_header_size_matches_static_size() {
assert_eq!(PacketHeader::SIZE, PacketHeader::ssize());
}
#[test]
fn packet_header_new_without_payload_builds_expected_values() {
let header: PacketHeader = PacketHeader::new(
&[] as &[TestBlock],
None::<&TestPayload>,
&mut default_payload_context(),
)
.expect("header without payload must be created");
assert_eq!(header.size, 0);
assert_eq!(header.blocks_len, 0);
assert!(!header.payload);
assert_eq!(header.crc.to_le_bytes(), header.crc());
}
#[test]
fn packet_header_get_pos_and_missing_bytes_work() {
let mut buf = vec![11_u8, 22, 33, 44];
let sig_pos = buf.len();
buf.extend_from_slice(&PACKET_SIG);
buf.extend_from_slice(&[0_u8; 5]);
assert_eq!(PacketHeader::get_pos(&buf), Some(sig_pos));
assert_eq!(PacketHeader::get_pos(&[1_u8, 2, 3]), None);
assert!(PacketHeader::is_not_enought(&buf).is_some());
assert_eq!(
PacketHeader::is_not_enought(&vec![0_u8; PacketHeader::SIZE as usize]),
None
);
assert_eq!(
PacketHeader::is_not_enought(&[0_u8; 3]),
Some(PacketHeader::SIZE as usize - 3)
);
}
}