use super::*;
use broadcast_common::len::need_out;
impl<'a> ExtensionBodyDef<'a> for ProtectionMessage<'a> {
const TAG_EXTENSION: u8 = 0x18;
const NAME: &'static str = "PROTECTION_MESSAGE";
}
pub const PROTECTION_MESSAGE_RESERVED_DEFAULT: u8 = 0x0F;
const RESERVED_MASK: u8 = 0x0F;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
#[non_exhaustive]
pub struct ProtectionMessage<'a> {
pub reserved: u8,
#[cfg_attr(feature = "serde", serde(borrow))]
pub component_tags: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub trailing: &'a [u8],
}
impl<'a> ProtectionMessage<'a> {
#[must_use]
pub const fn new(component_tags: &'a [u8]) -> Self {
Self {
reserved: PROTECTION_MESSAGE_RESERVED_DEFAULT,
component_tags,
trailing: &[],
}
}
}
impl<'a> Parse<'a> for ProtectionMessage<'a> {
type Error = crate::error::Error;
fn parse(sel: &'a [u8]) -> Result<Self> {
let first = *sel.first().ok_or(Error::BufferTooShort(
broadcast_common::error::BufferTooShort {
need: 1,
have: 0,
what: "protection_message body",
},
))?;
let reserved = (first >> 4) & RESERVED_MASK;
let component_count = usize::from(first & 0x0F);
let component_tags = sel
.get(1..1 + component_count)
.ok_or(Error::BufferTooShort(
broadcast_common::error::BufferTooShort {
need: 1 + component_count,
have: sel.len(),
what: "protection_message component_tags",
},
))?;
Ok(ProtectionMessage {
reserved,
component_tags,
trailing: &sel[1 + component_count..],
})
}
}
impl Serialize for ProtectionMessage<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
1 + self.component_tags.len() + self.trailing.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
need_out(buf, len)?;
let component_count = broadcast_common::len::fit_bits(
self.component_tags.len() as u64,
4,
"component_count",
)? as u8;
buf[0] = (broadcast_common::len::fit_mask(self.reserved, RESERVED_MASK, "reserved")? << 4)
| component_count;
let tags_end = 1 + self.component_tags.len();
buf[1..tags_end].copy_from_slice(self.component_tags);
buf[tags_end..len].copy_from_slice(self.trailing);
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::descriptors::extension::test_support::*;
use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
#[test]
fn parse_protection_message_structured() {
let sel = [0xF3, 0x10, 0x20, 0x30];
let bytes = wrap(0x18, &sel);
let d = ExtensionDescriptor::parse(&bytes).unwrap();
match &d.body {
ExtensionBody::ProtectionMessage(b) => {
assert_eq!(b.reserved, 0x0F);
assert_eq!(b.component_tags, &[0x10, 0x20, 0x30]);
}
other => panic!("expected ProtectionMessage, got {other:?}"),
}
round_trip(&d);
}
#[test]
fn parse_protection_message_empty() {
let sel = [0xF0]; let bytes = wrap(0x18, &sel);
let d = ExtensionDescriptor::parse(&bytes).unwrap();
match &d.body {
ExtensionBody::ProtectionMessage(b) => assert!(b.component_tags.is_empty()),
other => panic!("expected ProtectionMessage, got {other:?}"),
}
round_trip(&d);
}
#[test]
fn parse_tolerates_trailing_bytes() {
let bytes = wrap(0x18, &[0xF1, 0x10, 0xEE]);
let d = ExtensionDescriptor::parse(&bytes).unwrap();
match &d.body {
ExtensionBody::ProtectionMessage(b) => {
assert_eq!(b.component_tags, &[0x10]);
assert_eq!(b.trailing, &[0xEE]);
}
other => panic!("expected ProtectionMessage, got {other:?}"),
}
let mut buf = vec![0u8; d.serialized_len()];
d.serialize_into(&mut buf).unwrap();
assert_eq!(buf, bytes);
}
#[test]
fn protection_message_reserved_preserved() {
for r in 0u8..=RESERVED_MASK {
for tail in [&[][..], &[0xAA, 0xBB][..]] {
let mut sel = vec![(r << 4) | 2, 0x10, 0x20];
sel.extend_from_slice(tail);
let bytes = wrap(0x18, &sel);
let d = ExtensionDescriptor::parse(&bytes).unwrap();
let mut buf = vec![0u8; d.serialized_len()];
d.serialize_into(&mut buf).unwrap();
assert_eq!(buf, bytes, "r={r:#x}");
}
}
}
#[test]
fn protection_message_default_is_spec_value() {
let pm = ProtectionMessage::new(&[0x10]);
let mut buf = vec![0u8; pm.serialized_len()];
pm.serialize_into(&mut buf).unwrap();
assert_eq!(buf, [0xF1, 0x10]);
}
#[test]
fn serialize_rejects_oversized_component_list() {
let tags = [0u8; 16];
let pm = ProtectionMessage::new(&tags);
let mut buf = [0u8; 32];
assert!(matches!(
pm.serialize_into(&mut buf),
Err(Error::FieldOverflow(_))
));
}
}