use crate::crc::{crc_compute, params_for, CrcParams, CrcPreset};
use crate::e2e::{CheckInfo, E2eError, E2eProfile};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum P22Crc {
Crc16CcittFalse,
Crc32IsoHdlc,
Custom(CrcParams),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct P22Config {
pub data_id: u16,
pub crc: P22Crc,
}
pub struct Profile22 { cfg: P22Config }
impl Profile22 {
pub fn preset_crc16(data_id: u16) -> Self {
Self::new(P22Config { data_id, crc: P22Crc::Crc16CcittFalse })
}
pub fn preset_crc32(data_id: u16) -> Self {
Self::new(P22Config { data_id, crc: P22Crc::Crc32IsoHdlc })
}
pub fn preset_custom(data_id: u16, params: CrcParams) -> Self {
Self::new(P22Config { data_id, crc: P22Crc::Custom(params) })
}
pub fn new(cfg: P22Config) -> Self { Self { cfg } }
fn crc_params(&self) -> (CrcParams, usize) {
match self.cfg.crc {
P22Crc::Crc16CcittFalse => (params_for(CrcPreset::Crc16CcittFalse), 2),
P22Crc::Crc32IsoHdlc => (params_for(CrcPreset::Crc32IsoHdlc), 4),
P22Crc::Custom(p) => (p, (p.width / 8) as usize),
}
}
}
impl E2eProfile for Profile22 {
fn protect(&self, payload: &[u8], counter: u32) -> Result<Vec<u8>, E2eError> {
let (params, crc_bytes) = self.crc_params();
let length = payload.len() as u16;
let tail = 2 + 2 + 1 + crc_bytes;
if payload.len() + tail > 64 { return Err(E2eError::LengthError); }
let mut buf = Vec::with_capacity(payload.len() + tail);
buf.extend_from_slice(payload);
buf.extend_from_slice(&length.to_be_bytes());
buf.extend_from_slice(&self.cfg.data_id.to_be_bytes());
buf.push((counter & 0xFF) as u8);
let crc = crc_compute(params, &buf);
for i in (0..crc_bytes).rev() {
buf.push(((crc >> (i * 8)) & 0xFF) as u8);
}
Ok(buf)
}
fn check(&self, frame: &[u8]) -> Result<CheckInfo, E2eError> {
let (params, crc_bytes) = self.crc_params();
if frame.len() < (2 + 2 + 1 + crc_bytes) { return Err(E2eError::LengthError); }
let len = frame.len();
let crc_recv = &frame[len - crc_bytes..];
let counter = frame[len - crc_bytes - 1];
let data_id = u16::from_be_bytes([frame[len - crc_bytes - 3], frame[len - crc_bytes - 2]]);
let length = u16::from_be_bytes([frame[len - crc_bytes - 5], frame[len - crc_bytes - 4]]) as usize;
let payload = &frame[..len - (2 + 2 + 1 + crc_bytes)];
if payload.len() != length { return Err(E2eError::LengthError); }
let want = crc_compute(params, &frame[..len - crc_bytes]);
for i in 0..crc_bytes {
let b = ((want >> ((crc_bytes - 1 - i) * 8)) & 0xFF) as u8;
if b != crc_recv[i] { return Err(E2eError::CrcMismatch); }
}
if data_id != self.cfg.data_id { return Err(E2eError::DataIdMismatch); }
Ok(CheckInfo { counter: counter as u32, ok: true })
}
fn payload_len(&self, frame: &[u8]) -> Option<usize> {
let (params, crc_bytes) = self.crc_params();
let _ = params; if frame.len() < (2 + 2 + 1 + crc_bytes) { return None; }
let len = frame.len();
let length = u16::from_be_bytes([frame[len - crc_bytes - 5], frame[len - crc_bytes - 4]]) as usize;
(frame.len() >= length + (2 + 2 + 1 + crc_bytes)).then_some(length)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{E2eSession, CounterPolicy, E2eProfile, E2eError};
#[test]
fn p22_roundtrip_crc16() {
let mut tx = E2eSession::new(
Profile22::preset_crc16(0xBEEF),
CounterPolicy::MonotonicRollover { bits: 8, step: 1 },
);
let payload = vec![0xAA; 20];
let f = tx.wrap(&payload).unwrap();
let mut rx = E2eSession::new(
Profile22::preset_crc16(0xBEEF),
CounterPolicy::MonotonicRollover { bits: 8, step: 1 },
);
let pl = rx.unwrap(&f).unwrap();
assert_eq!(pl, &payload[..]);
}
#[test]
fn p22_roundtrip_crc32() {
let mut tx = E2eSession::new(
Profile22::preset_crc32(0x00FE),
CounterPolicy::MonotonicRollover { bits: 8, step: 1 },
);
let payload = (0u8..40).collect::<Vec<_>>();
let f = tx.wrap(&payload).unwrap();
let mut rx = E2eSession::new(
Profile22::preset_crc32(0x00FE),
CounterPolicy::MonotonicRollover { bits: 8, step: 1 },
);
let pl = rx.unwrap(&f).unwrap();
assert_eq!(pl, &payload[..]);
}
#[test]
fn p22_detect_length_and_crc() {
let p = Profile22::preset_crc16(0x0102);
let payload = vec![1,2,3,4,5,6];
let mut f = p.protect(&payload, 3).unwrap();
f.remove(0);
assert!(matches!(p.check(&f), Err(E2eError::LengthError)));
let mut f2 = Profile22::preset_crc32(0x0102).protect(&payload, 7).unwrap();
f2[0] ^= 0x01;
assert!(matches!(Profile22::preset_crc32(0x0102).check(&f2), Err(E2eError::CrcMismatch)));
}
}