use crc::{CRC_16_IBM_3740, CRC_32_AUTOSAR, Crc};
const CRC32_P4: Crc<u32> = Crc::<u32>::new(&CRC_32_AUTOSAR);
const CRC16_CCITT: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
pub fn compute_crc32_p4(length: u16, counter: u16, data_id: u32, payload: &[u8]) -> u32 {
let mut digest = CRC32_P4.digest();
digest.update(&length.to_be_bytes());
digest.update(&counter.to_be_bytes());
digest.update(&data_id.to_be_bytes());
digest.update(payload);
digest.finalize()
}
pub fn compute_crc16_p5(data_id: u16, counter: u8, payload: &[u8]) -> u16 {
crate::log::trace!(
"CRC-16 Profile5: data_id=0x{:04X}, counter={}, payload_len={}, payload={:02X?}",
data_id,
counter,
payload.len(),
payload
);
let mut digest = CRC16_CCITT.digest();
digest.update(&[counter]);
digest.update(payload);
let data_id_bytes = data_id.to_le_bytes();
digest.update(&data_id_bytes);
let crc = digest.finalize();
crate::log::trace!(
"CRC-16 Profile5: computed CRC = 0x{:04X} (bytes: {:02X?})",
crc,
crc.to_le_bytes()
);
crc
}
pub fn compute_crc16_p5_with_header(
data_id: u16,
counter: u8,
payload: &[u8],
upper_header: [u8; 8],
) -> u16 {
crate::log::trace!(
"CRC-16 Profile5 (with header): data_id=0x{:04X}, counter={}, payload_len={}, upper_header={:02X?}, payload={:02X?}",
data_id,
counter,
payload.len(),
upper_header,
payload
);
let mut digest = CRC16_CCITT.digest();
digest.update(&upper_header);
digest.update(&[counter]);
digest.update(payload);
digest.update(&data_id.to_le_bytes());
let crc = digest.finalize();
crate::log::trace!(
"CRC-16 Profile5 (with header): computed CRC = 0x{:04X} (bytes: {:02X?})",
crc,
crc.to_le_bytes()
);
crc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crc32_p4_basic() {
let crc1 = compute_crc32_p4(10, 0, 0x1234_5678, b"test");
let crc2 = compute_crc32_p4(10, 1, 0x1234_5678, b"test");
let crc3 = compute_crc32_p4(10, 0, 0x1234_5679, b"test");
let crc4 = compute_crc32_p4(10, 0, 0x1234_5678, b"Test");
assert_ne!(crc1, crc2, "Different counter should produce different CRC");
assert_ne!(crc1, crc3, "Different data_id should produce different CRC");
assert_ne!(crc1, crc4, "Different payload should produce different CRC");
}
#[test]
fn test_crc16_p5_basic() {
let crc1 = compute_crc16_p5(0x1234, 0, b"test");
let crc2 = compute_crc16_p5(0x1234, 1, b"test");
let crc3 = compute_crc16_p5(0x1235, 0, b"test");
let crc4 = compute_crc16_p5(0x1234, 0, b"Test");
assert_ne!(crc1, crc2, "Different counter should produce different CRC");
assert_ne!(crc1, crc3, "Different data_id should produce different CRC");
assert_ne!(crc1, crc4, "Different payload should produce different CRC");
}
#[test]
fn test_crc32_p4_deterministic() {
let crc1 = compute_crc32_p4(20, 5, 0xABCD_EF01, b"payload data");
let crc2 = compute_crc32_p4(20, 5, 0xABCD_EF01, b"payload data");
assert_eq!(crc1, crc2);
}
#[test]
fn test_crc16_p5_deterministic() {
let crc1 = compute_crc16_p5(0xABCD, 5, b"payload data");
let crc2 = compute_crc16_p5(0xABCD, 5, b"payload data");
assert_eq!(crc1, crc2);
}
#[test]
fn test_crc32_p4_empty_payload() {
let crc = compute_crc32_p4(8, 0, 0x1234_5678, b"");
assert_ne!(crc, 0); }
#[test]
fn test_crc16_p5_empty_payload() {
let crc = compute_crc16_p5(0x1234, 0, b"");
assert_ne!(crc, 0); }
#[test]
fn test_crc16_p5_with_header_nonzero_header_changes_crc() {
let header = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let crc_no_header = compute_crc16_p5(0x1234, 0, b"test");
let crc_with_header = compute_crc16_p5_with_header(0x1234, 0, b"test", header);
assert_ne!(
crc_no_header, crc_with_header,
"Non-zero upper_header should change CRC vs headerless path"
);
}
#[test]
fn test_crc16_p5_with_header_different_headers_differ() {
let header_a = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let header_b = [0x00, 0x02, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00]; let crc_a = compute_crc16_p5_with_header(0x1234, 0, b"test", header_a);
let crc_b = compute_crc16_p5_with_header(0x1234, 0, b"test", header_b);
assert_ne!(
crc_a, crc_b,
"Different upper_header should produce different CRC"
);
}
#[test]
fn test_crc16_p5_with_header_deterministic() {
let header = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let crc1 = compute_crc16_p5_with_header(0x1234, 0, b"test", header);
let crc2 = compute_crc16_p5_with_header(0x1234, 0, b"test", header);
assert_eq!(crc1, crc2, "Same inputs should always produce same CRC");
}
#[test]
fn test_crc16_p5_with_header_each_field_matters() {
let header = [0x00, 0x01, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00];
let baseline = compute_crc16_p5_with_header(0x1234, 0, b"test", header);
let crc_diff_data_id = compute_crc16_p5_with_header(0x1235, 0, b"test", header);
let crc_diff_counter = compute_crc16_p5_with_header(0x1234, 1, b"test", header);
let crc_diff_payload = compute_crc16_p5_with_header(0x1234, 0, b"Test", header);
assert_ne!(
baseline, crc_diff_data_id,
"Different data_id should change CRC"
);
assert_ne!(
baseline, crc_diff_counter,
"Different counter should change CRC"
);
assert_ne!(
baseline, crc_diff_payload,
"Different payload should change CRC"
);
}
}