dvb-si 13.0.0

ETSI EN 300 468 DVB Service Information parser + builder. MPEG-2 PSI included.
Documentation
//! CP Identifier Descriptor — ETSI EN 300 468 V1.20.1 §6.4.4, Table 114 (tag_extension 0x03).
//!
//! Lists the CP_system_ids of the content protection systems available for the
//! associated service/bouquet — a loop of 16-bit identifiers.
use super::*;
use alloc::vec::Vec;
use broadcast_common::len::need_out;

impl ExtensionBodyDef<'_> for CpIdentifier {
    const TAG_EXTENSION: u8 = 0x03;
    const NAME: &'static str = "CP_IDENTIFIER";
}

/// CP_identifier descriptor body (Table 114, §6.4.4).
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct CpIdentifier {
    /// The CP_system_id loop.
    pub cp_system_ids: Vec<u16>,
}

impl<'a> Parse<'a> for CpIdentifier {
    type Error = crate::error::Error;
    fn parse(sel: &'a [u8]) -> Result<Self> {
        if !sel.len().is_multiple_of(2) {
            return Err(Error::InvalidDescriptor {
                tag: crate::descriptor_tag::DescriptorTag::Extension as u8,
                reason: "CP_identifier body length must be a multiple of 2",
            });
        }
        let cp_system_ids = sel
            .chunks_exact(2)
            .map(|c| u16::from_be_bytes([c[0], c[1]]))
            .collect();
        Ok(CpIdentifier { cp_system_ids })
    }
}

impl Serialize for CpIdentifier {
    type Error = crate::error::Error;
    fn serialized_len(&self) -> usize {
        self.cp_system_ids.len() * 2
    }
    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
        let len = self.serialized_len();
        need_out(buf, len)?;
        for (i, id) in self.cp_system_ids.iter().enumerate() {
            buf[i * 2..i * 2 + 2].copy_from_slice(&id.to_be_bytes());
        }
        Ok(len)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::descriptors::extension::test_support::*;
    use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor, ExtensionTag};

    #[test]
    fn parse_cp_identifier() {
        let sel = [0x12, 0x34, 0x56, 0x78];
        let bytes = wrap(0x03, &sel);
        let d = ExtensionDescriptor::parse(&bytes).unwrap();
        assert_eq!(d.kind(), Some(ExtensionTag::CpIdentifier));
        match &d.body {
            ExtensionBody::CpIdentifier(b) => {
                assert_eq!(b.cp_system_ids, alloc::vec![0x1234, 0x5678]);
            }
            other => panic!("expected CpIdentifier, got {other:?}"),
        }
        round_trip(&d);
    }

    #[test]
    fn parse_cp_identifier_rejects_odd_length() {
        let err = CpIdentifier::parse(&[0x00, 0x00, 0x00]).unwrap_err();
        assert!(matches!(err, Error::InvalidDescriptor { .. }));
    }
}