mod bytes;
mod decoder;
mod integrity;
mod packets;
mod pn;
mod synchronizer;
pub use decoder::*;
pub use integrity::*;
pub use packets::*;
pub use pn::*;
pub use synchronizer::*;
use serde::{Deserialize, Serialize};
pub type Scid = u16;
pub type Vcid = u16;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct VCDUHeader {
pub version: u8,
pub scid: Scid,
pub vcid: Vcid,
pub counter: u32,
pub replay: bool,
pub cycle: bool,
pub counter_cycle: u8,
}
impl VCDUHeader {
pub const LEN: usize = 6;
pub const FILL: Vcid = 63;
pub const COUNTER_MAX: u32 = 0xff_ffff - 1;
#[must_use]
pub fn decode(dat: &[u8]) -> Option<Self> {
if dat.len() < Self::LEN {
return None;
}
let x = u16::from_be_bytes([dat[0], dat[1]]);
Some(VCDUHeader {
version: (dat[0] >> 6) & 0x3,
scid: ((x >> 6) & 0xff),
vcid: (x & 0x3f),
counter: u32::from_be_bytes([0, dat[2], dat[3], dat[4]]),
replay: (dat[5] >> 7) & 0x1 == 1,
cycle: (dat[5] >> 6) & 0x1 == 1,
counter_cycle: dat[5] & 0xf,
})
}
}
#[derive(Clone)]
pub struct MPDU {
first_header: u16,
data: Vec<u8>,
}
impl std::fmt::Debug for MPDU {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"MPDU {{ fill:{} fhp:{:#x} }}",
self.is_fill(),
self.header_offset()
)
}
}
impl MPDU {
pub const FILL: u16 = 0x7fe;
pub const NO_HEADER: u16 = 0x7ff;
#[must_use]
pub fn decode(data: &[u8]) -> Option<Self> {
if data.len() < 2 {
return None;
}
let x = u16::from_be_bytes([data[0], data[1]]);
Some(MPDU {
first_header: x & 0x7ff,
data: data.to_vec(),
})
}
#[must_use]
pub fn is_fill(&self) -> bool {
self.first_header == Self::FILL
}
#[must_use]
pub fn has_header(&self) -> bool {
self.first_header != Self::NO_HEADER
}
#[must_use]
pub fn payload(&self) -> &[u8] {
assert!(self.data.len() >= 2, "mpdu data too short");
&self.data[2..]
}
#[must_use]
pub fn header_offset(&self) -> usize {
self.first_header as usize
}
}
#[derive(Debug, Clone)]
pub struct Frame {
pub header: VCDUHeader,
pub data: Vec<u8>,
}
impl Frame {
#[must_use]
pub fn decode(dat: Vec<u8>) -> Option<Self> {
let header = VCDUHeader::decode(&dat)?;
Some(Frame { header, data: dat })
}
#[must_use]
pub fn is_fill(&self) -> bool {
self.header.vcid == VCDUHeader::FILL
}
#[must_use]
pub fn mpdu(&self, izone_length: usize, trailer_length: usize) -> Option<MPDU> {
let start: usize = VCDUHeader::LEN + izone_length;
let end: usize = self.data.len() - trailer_length;
let data = self.data[start..end].to_vec();
MPDU::decode(&data)
}
}
#[must_use]
pub fn missing_frames(cur: u32, last: u32) -> u32 {
if cur == last {
return VCDUHeader::COUNTER_MAX;
}
let expected = if last == VCDUHeader::COUNTER_MAX {
0
} else {
last + 1
};
if cur == expected {
0
} else {
if cur < last {
return VCDUHeader::COUNTER_MAX - last + cur;
}
cur - last - 1
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn decode_vcduheader() {
let dat: Vec<u8> = vec![
0x55, 0x61, 0x01, 0xe2, 0x40, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xaa, ];
let header = VCDUHeader::decode(&dat).unwrap();
assert_eq!(header.version, 1);
assert_eq!(header.scid, 85);
assert_eq!(header.vcid, 33);
assert_eq!(header.counter, 123_456);
assert!(!header.replay);
assert!(!header.cycle);
assert_eq!(header.counter_cycle, 5);
}
#[test]
fn decode_vcduheader_minmax() {
let dat: Vec<u8> = vec![0, 0, 0, 0, 0, 0];
VCDUHeader::decode(&dat).unwrap();
let dat: Vec<u8> = vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
VCDUHeader::decode(&dat).unwrap();
}
#[test]
fn decode_vcduheader_is_err_when_data_too_short() {
let zult = VCDUHeader::decode(&[0u8; 0]);
assert!(zult.is_none());
}
#[test]
fn test_missing_frames() {
assert_eq!(missing_frames(5, 4), 0);
assert_eq!(missing_frames(5, 3), 1);
assert_eq!(missing_frames(0, VCDUHeader::COUNTER_MAX), 0);
assert_eq!(missing_frames(0, VCDUHeader::COUNTER_MAX - 1), 1);
assert_eq!(missing_frames(0, 0), VCDUHeader::COUNTER_MAX);
}
}