use crate::error::{Error, Result};
use dvb_common::crc32_mpeg2;
pub const CRC_LEN: usize = 4;
pub fn validate_crc(bytes: &[u8]) -> Result<()> {
if bytes.len() < CRC_LEN {
return Err(Error::Truncated);
}
let (payload, trailer) = bytes.split_at(bytes.len() - CRC_LEN);
let expected = u32::from_be_bytes(trailer.try_into().unwrap());
let computed = crc32_mpeg2::compute(payload);
if computed == expected {
Ok(())
} else {
Err(Error::InvalidCrc { expected, computed })
}
}
#[cfg(test)]
mod tests {
use super::*;
use dvb_common::crc32_mpeg2::{compute, TABLE};
#[test]
fn crc32_of_empty_input_is_initial_state() {
assert_eq!(compute(&[]), 0xFFFF_FFFF);
}
#[test]
fn table_first_entry_is_zero() {
assert_eq!(TABLE[0], 0);
}
#[test]
fn single_zero_byte_yields_index_ff() {
let crc = compute(&[0x00]);
let expected = TABLE[0xFF] ^ 0xFFFF_FF00;
assert_eq!(crc, expected);
}
#[test]
fn validate_crc_passes_for_known_packet() {
let data = [0x01, 0x02, 0x03, 0x04];
let c = compute(&data);
let mut buf = Vec::with_capacity(data.len() + CRC_LEN);
buf.extend_from_slice(&data);
buf.extend_from_slice(&c.to_be_bytes());
assert!(validate_crc(&buf).is_ok());
}
#[test]
fn validate_crc_fails_for_corrupted_trailer() {
let data = [0x01, 0x02, 0x03, 0x04];
let c = compute(&data);
let mut buf = Vec::with_capacity(data.len() + CRC_LEN);
buf.extend_from_slice(&data);
buf.extend_from_slice(&c.to_be_bytes());
*buf.last_mut().unwrap() ^= 0xFF;
assert!(validate_crc(&buf).is_err());
}
#[test]
fn validate_crc_fails_for_corrupted_data() {
let data = [0x01, 0x02, 0x03, 0x04];
let c = compute(&data);
let mut buf = Vec::with_capacity(data.len() + CRC_LEN);
buf.extend_from_slice(&data);
buf.extend_from_slice(&c.to_be_bytes());
buf[0] ^= 0xFF;
assert!(validate_crc(&buf).is_err());
}
#[test]
fn validate_crc_rejects_too_short_input() {
assert!(validate_crc(&[0x01, 0x02]).is_err());
}
}