use crate::{Confidence, Detail, Evidence, Outcome};
const MXF_KEY_PREFIX: [u8; 7] = [0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01];
const MXF_KEY_MID: [u8; 4] = [0x0D, 0x01, 0x02, 0x01];
const MXF_KEY_STRUCTURE_KIND: u8 = 0x01;
const MXF_PARTITION_KIND_HEADER: u8 = 0x02;
const MXF_PARTITION_KIND_BODY: u8 = 0x03;
const MXF_PARTITION_KIND_FOOTER: u8 = 0x04;
fn partition_kind_from_byte(b: u8) -> crate::PartitionKind {
match b {
MXF_PARTITION_KIND_HEADER => crate::PartitionKind::Header,
MXF_PARTITION_KIND_BODY => crate::PartitionKind::Body,
MXF_PARTITION_KIND_FOOTER => crate::PartitionKind::Footer,
other => crate::PartitionKind::Other(other),
}
}
const UL_KEY_LEN: usize = 16;
const BER_LONG_COUNT_MASK: u8 = 0x7F;
const BER_LONG_FORM_FLAG: u8 = 0x80;
const BER_LONG_MAX_OCTETS: usize = 8;
pub(crate) fn probe(data: &[u8], limit: usize) -> Outcome {
debug_assert!(limit <= data.len(), "harness caps limit at data.len()");
let region = &data[..limit];
if region.len() < UL_KEY_LEN {
return Outcome::Insufficient(UL_KEY_LEN);
}
if region[..MXF_KEY_PREFIX.len()] != MXF_KEY_PREFIX {
return Outcome::None;
}
let ber = ®ion[UL_KEY_LEN..];
let detail = Detail::Mxf {
partition_kind: partition_kind_from_byte(region[13]),
};
if is_partition_pack_key(region) && ber_length_well_formed(ber) {
Outcome::Match(Evidence {
confidence: Confidence::CERTAIN,
detail,
})
} else {
Outcome::Match(Evidence {
confidence: Confidence::STRONG,
detail,
})
}
}
fn is_partition_pack_key(key: &[u8]) -> bool {
key[..MXF_KEY_PREFIX.len()] == MXF_KEY_PREFIX
&& key[8..8 + MXF_KEY_MID.len()] == MXF_KEY_MID
&& key[12] == MXF_KEY_STRUCTURE_KIND
&& (MXF_PARTITION_KIND_HEADER..=MXF_PARTITION_KIND_FOOTER).contains(&key[13])
}
fn ber_length_well_formed(ber: &[u8]) -> bool {
let Some(&first) = ber.first() else {
return false;
};
if first & BER_LONG_FORM_FLAG == 0 {
return true;
}
let octets = (first & BER_LONG_COUNT_MASK) as usize;
if !(1..=BER_LONG_MAX_OCTETS).contains(&octets) {
return false;
}
ber.len() > octets
}
#[cfg(test)]
mod drift {
#[test]
fn ul_header_matches_st377_1() {
use st377_1::FILL_ITEM_KEY_PREFIX;
use st377_1::op1a::OP1A_UL_PREFIX;
assert_eq!(
super::MXF_KEY_PREFIX[..4],
OP1A_UL_PREFIX[..4],
"container-probe's MXF UL header has drifted from st377-1's"
);
assert_eq!(
super::MXF_KEY_PREFIX[..4],
FILL_ITEM_KEY_PREFIX[..4],
"container-probe's MXF UL header has drifted from st377-1's"
);
}
#[test]
fn partition_key_predicate_matches_st377_1() {
let mut base = [0u8; 16];
base[0..7].copy_from_slice(&super::MXF_KEY_PREFIX);
base[7] = 0x01;
base[8..12].copy_from_slice(&super::MXF_KEY_MID);
base[12] = 0x01;
base[13] = 0x02; base[14] = 0x04; base[15] = 0x00;
assert_eq!(
super::is_partition_pack_key(&base),
st377_1::PartitionPack::is_partition_key(&base),
);
let real = std::fs::read(std::format!(
"{}/../fixtures/mxf/op1a_mpeg2_pcm.mxf",
env!("CARGO_MANIFEST_DIR")
))
.expect("fixture");
let real_key: [u8; 16] = real[..16].try_into().expect("16 bytes");
assert_eq!(
super::is_partition_pack_key(&real_key),
st377_1::PartitionPack::is_partition_key(&real_key),
);
assert!(
super::is_partition_pack_key(&real_key),
"real fixture must be a partition pack"
);
for pos in [8usize, 9, 10, 11, 12, 13] {
for val in [0x00u8, 0x01, 0xFF] {
let mut m = base;
m[pos] = val;
assert_eq!(
super::is_partition_pack_key(&m),
st377_1::PartitionPack::is_partition_key(&m),
"predicates disagree at byte {pos}={val:#x}"
);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fixture_bytes(rel: &str) -> std::vec::Vec<u8> {
std::fs::read(std::format!("{}/../{}", env!("CARGO_MANIFEST_DIR"), rel))
.unwrap_or_else(|e| panic!("failed to read {rel}: {e}"))
}
#[test]
fn short_prefix_is_insufficient() {
let data = fixture_bytes("fixtures/mxf/op1a_mpeg2_pcm.mxf");
let region = &data[..UL_KEY_LEN - 1];
match probe(region, region.len()) {
Outcome::Insufficient(n) => assert_eq!(n, UL_KEY_LEN),
other => panic!("15-byte MXF prefix must be Insufficient(16), got {other:?}"),
}
}
#[test]
fn prefix_but_not_partition_key_is_strong_not_certain() {
let mut key = [0u8; 32]; key[0..7].copy_from_slice(&MXF_KEY_PREFIX);
key[7] = 0x01;
key[8..12].copy_from_slice(&[0x00, 0x00, 0x00, 0x00]);
key[12] = 0x01;
key[13] = 0x02;
key[16] = 0x10;
match probe(&key, key.len()) {
Outcome::Match(ev) => assert_eq!(ev.confidence, Confidence::STRONG),
other => panic!("prefix-only MXF must be STRONG, got {other:?}"),
}
}
}