pub(crate) const CRC_OFFSET: usize = 0;
pub(crate) const LEN_OFFSET: usize = 4;
pub(crate) const HEADER_LEN: usize = 8;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Header {
pub crc: u32,
pub len: u32,
}
#[inline]
pub(crate) fn framed_len(payload_len: usize) -> usize {
HEADER_LEN + payload_len
}
#[inline]
pub(crate) fn encode(buf: &mut Vec<u8>, payload: &[u8]) {
debug_assert!(
payload.len() <= u32::MAX as usize,
"payload length must fit in u32"
);
buf.clear();
buf.reserve(framed_len(payload.len()));
buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&(payload.len() as u32).to_le_bytes());
buf.extend_from_slice(payload);
let crc = crc32c::crc32c(&buf[LEN_OFFSET..]);
buf[CRC_OFFSET..LEN_OFFSET].copy_from_slice(&crc.to_le_bytes());
}
#[inline]
pub(crate) fn parse_header(bytes: &[u8; HEADER_LEN]) -> Header {
let crc = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let len = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
Header { crc, len }
}
#[inline]
pub(crate) fn verify(header_bytes: &[u8; HEADER_LEN], payload: &[u8], expected_crc: u32) -> bool {
let partial = crc32c::crc32c(&header_bytes[LEN_OFFSET..HEADER_LEN]);
let crc = crc32c::crc32c_append(partial, payload);
crc == expected_crc
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
fn header_array(buf: &[u8]) -> [u8; HEADER_LEN] {
buf[..HEADER_LEN].try_into().unwrap()
}
#[test]
fn test_encode_layout_and_roundtrip() {
let mut buf = Vec::new();
encode(&mut buf, b"hello");
assert_eq!(buf.len(), framed_len(5));
let header = parse_header(&header_array(&buf));
assert_eq!(header.len, 5);
let payload = &buf[HEADER_LEN..];
assert_eq!(payload, b"hello");
assert!(verify(&header_array(&buf), payload, header.crc));
}
#[test]
fn test_encode_empty_payload() {
let mut buf = Vec::new();
encode(&mut buf, b"");
assert_eq!(buf.len(), HEADER_LEN);
let header = parse_header(&header_array(&buf));
assert_eq!(header.len, 0);
assert!(verify(&header_array(&buf), &[], header.crc));
}
#[test]
fn test_streaming_crc_equals_contiguous_crc() {
let mut buf = Vec::new();
encode(&mut buf, b"some bytes here");
let header = parse_header(&header_array(&buf));
let contiguous = crc32c::crc32c(&buf[LEN_OFFSET..]);
assert_eq!(contiguous, header.crc);
}
#[test]
fn test_flipped_payload_byte_fails_verify() {
let mut buf = Vec::new();
encode(&mut buf, b"payload");
let header = parse_header(&header_array(&buf));
let mut payload = buf[HEADER_LEN..].to_vec();
payload[0] ^= 0x01;
assert!(!verify(&header_array(&buf), &payload, header.crc));
}
#[test]
fn test_flipped_length_byte_fails_verify() {
let mut buf = Vec::new();
encode(&mut buf, b"payload");
buf[LEN_OFFSET] ^= 0x01;
let header = parse_header(&header_array(&buf));
let payload = &buf[HEADER_LEN..];
assert!(!verify(&header_array(&buf), payload, header.crc));
}
#[test]
fn test_reused_buffer_does_not_leak_previous_record() {
let mut buf = Vec::new();
encode(&mut buf, b"a longer first record");
encode(&mut buf, b"short");
assert_eq!(buf.len(), framed_len(5));
let header = parse_header(&header_array(&buf));
assert_eq!(&buf[HEADER_LEN..], b"short");
assert!(verify(&header_array(&buf), &buf[HEADER_LEN..], header.crc));
}
}