const AFC_BYTE: usize = 3;
const PAYLOAD_FLAG: u8 = 0x10;
const ADAPTATION_FLAG: u8 = 0x20;
const AF_LEN_BYTE: usize = 4;
const AF_FLAGS_BYTE: usize = 5;
const PCR_FLAG: u8 = 0x10;
const PCR_FIELD_START: usize = AF_FLAGS_BYTE + 1;
const PCR_FIELD_LEN: usize = 6;
fn pcr_field_range(pkt: &[u8]) -> Option<(usize, usize)> {
if pkt.len() <= AF_FLAGS_BYTE {
return None;
}
if pkt[AFC_BYTE] & ADAPTATION_FLAG == 0 {
return None;
}
let af_len = pkt[AF_LEN_BYTE] as usize;
if af_len == 0 {
return None;
}
if pkt[AF_FLAGS_BYTE] & PCR_FLAG == 0 {
return None;
}
let end = PCR_FIELD_START + PCR_FIELD_LEN;
if end > pkt.len() {
return None;
}
Some((PCR_FIELD_START, end))
}
pub fn is_legal_duplicate_pair(prev: &[u8], curr: &[u8]) -> bool {
if prev.len() != curr.len() || prev.len() <= AFC_BYTE {
return false;
}
if curr[AFC_BYTE] & PAYLOAD_FLAG == 0 {
return false;
}
let pcr_range = pcr_field_range(curr);
for i in 0..prev.len() {
let in_pcr_field = matches!(pcr_range, Some((start, end)) if i >= start && i < end);
if !in_pcr_field && prev[i] != curr[i] {
return false;
}
}
true
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DuplicateVerdict {
NotDuplicate,
Legal,
IllegalThirdRepeat,
}
impl DuplicateVerdict {
pub fn name(&self) -> &'static str {
match self {
Self::NotDuplicate => "not duplicate",
Self::Legal => "legal",
Self::IllegalThirdRepeat => "illegal third repeat",
}
}
}
crate::impl_spec_display!(DuplicateVerdict);
pub fn check_duplicate(prev: &[u8], curr: &[u8], dup_already_used: bool) -> DuplicateVerdict {
if !is_legal_duplicate_pair(prev, curr) {
return DuplicateVerdict::NotDuplicate;
}
if dup_already_used {
DuplicateVerdict::IllegalThirdRepeat
} else {
DuplicateVerdict::Legal
}
}
#[cfg(test)]
mod tests {
use super::*;
fn payload_packet(pid: u16, afc_cc: u8, fill: u8) -> [u8; 188] {
let mut pkt = [fill; 188];
pkt[0] = 0x47;
pkt[1] = ((pid >> 8) as u8) & 0x1F;
pkt[2] = (pid & 0xFF) as u8;
pkt[3] = afc_cc;
pkt
}
fn packet_with_adaptation(
pid: u16,
afc_cc: u8,
pcr: [u8; 6],
opcr: Option<[u8; 6]>,
splice_countdown: Option<u8>,
fill: u8,
) -> [u8; 188] {
let mut pkt = [fill; 188];
pkt[0] = 0x47;
pkt[1] = ((pid >> 8) as u8) & 0x1F;
pkt[2] = (pid & 0xFF) as u8;
pkt[3] = afc_cc | ADAPTATION_FLAG;
let mut flags = PCR_FLAG;
let mut body_len = 1 + 6; if opcr.is_some() {
flags |= 0x08; body_len += 6;
}
if splice_countdown.is_some() {
flags |= 0x04; body_len += 1;
}
pkt[4] = body_len as u8; pkt[5] = flags;
pkt[6..12].copy_from_slice(&pcr);
let mut cursor = 12usize;
if let Some(o) = opcr {
pkt[cursor..cursor + 6].copy_from_slice(&o);
cursor += 6;
}
if let Some(sc) = splice_countdown {
pkt[cursor] = sc;
cursor += 1;
}
let _ = cursor;
pkt
}
#[test]
fn byte_identical_is_legal() {
let a = payload_packet(0x0100, 0x10, 0xAB);
let b = a;
assert!(is_legal_duplicate_pair(&a, &b));
}
#[test]
fn pcr_only_difference_is_legal() {
let a = packet_with_adaptation(0x0100, 0x10, [0, 0, 0, 0, 0, 0], None, None, 0xAB);
let mut b = a;
b[6..12].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]);
assert!(is_legal_duplicate_pair(&a, &b));
let mut c = a;
c[6..12].copy_from_slice(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]);
c[20] ^= 0xFF;
assert!(!is_legal_duplicate_pair(&a, &c));
}
#[test]
fn payload_difference_is_illegal() {
let a = payload_packet(0x0100, 0x10, 0xAB);
let mut b = a;
b[100] ^= 0xFF; assert!(!is_legal_duplicate_pair(&a, &b));
}
#[test]
fn splice_countdown_difference_is_illegal() {
let a = packet_with_adaptation(0x0100, 0x10, [0, 0, 0, 0, 0, 0], None, Some(5), 0xCD);
let mut b = a;
b[12] ^= 0x01;
assert!(!is_legal_duplicate_pair(&a, &b));
}
#[test]
fn opcr_difference_is_illegal() {
let a = packet_with_adaptation(
0x0100,
0x10,
[0, 0, 0, 0, 0, 0],
Some([1, 2, 3, 4, 5, 6]),
None,
0xCD,
);
let mut b = a;
b[12] ^= 0x01;
assert!(!is_legal_duplicate_pair(&a, &b));
}
#[test]
fn wrong_adaptation_field_control_is_not_a_duplicate() {
let a = payload_packet(0x0100, 0x00, 0xAB);
let b = a;
assert!(!is_legal_duplicate_pair(&a, &b));
let mut c = packet_with_adaptation(0x0100, 0x00, [0; 6], None, None, 0xAB);
c[3] &= !PAYLOAD_FLAG; let d = c;
assert!(!is_legal_duplicate_pair(&c, &d));
}
#[test]
fn third_consecutive_repeat_is_illegal() {
let a = payload_packet(0x0100, 0x10, 0xAB);
let b = a;
assert_eq!(check_duplicate(&a, &b, false), DuplicateVerdict::Legal);
let c = a;
assert_eq!(
check_duplicate(&b, &c, true),
DuplicateVerdict::IllegalThirdRepeat
);
}
#[test]
fn not_a_duplicate_reports_not_duplicate_regardless_of_state() {
let a = payload_packet(0x0100, 0x10, 0xAB);
let mut b = a;
b[50] ^= 0xFF;
assert_eq!(
check_duplicate(&a, &b, false),
DuplicateVerdict::NotDuplicate
);
assert_eq!(
check_duplicate(&a, &b, true),
DuplicateVerdict::NotDuplicate
);
}
#[test]
fn different_lengths_are_never_duplicates() {
let a = [0u8; 188];
let b = [0u8; 187];
assert!(!is_legal_duplicate_pair(&a, &b));
}
#[test]
fn too_short_for_afc_byte_is_never_a_duplicate() {
let a = [0x47, 0x00, 0x10];
let b = a;
assert!(!is_legal_duplicate_pair(&a, &b));
}
}