dvb-si 13.0.0

ETSI EN 300 468 DVB Service Information parser + builder. MPEG-2 PSI included.
Documentation
//! Service Relocated Descriptor — ETSI EN 300 468 §6.4.10 (tag_extension 0x0B).
use super::*;
use broadcast_common::len::need_out;

impl ExtensionBodyDef<'_> for ServiceRelocated {
    const TAG_EXTENSION: u8 = 0x0B;
    const NAME: &'static str = "SERVICE_RELOCATED";
}
/// service_relocated body (Table 152) — fully typed, fixed 6 bytes.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceRelocated {
    /// old_original_network_id(16).
    pub old_original_network_id: u16,
    /// old_transport_stream_id(16).
    pub old_transport_stream_id: u16,
    /// old_service_id(16).
    pub old_service_id: u16,
}

impl<'a> Parse<'a> for ServiceRelocated {
    type Error = crate::error::Error;
    fn parse(sel: &'a [u8]) -> Result<Self> {
        // Exactly the three fixed u16 fields (EN 300 468 §6.2.16): a longer
        // selector has trailing bytes with no wire meaning, which used to be
        // dropped on re-serialize (r03-W9).
        let (hdr, rest) =
            sel.split_first_chunk::<SERVICE_RELOCATED_LEN>()
                .ok_or(Error::BufferTooShort(
                    broadcast_common::error::BufferTooShort {
                        need: SERVICE_RELOCATED_LEN,
                        have: sel.len(),
                        what: "service_relocated body",
                    },
                ))?;
        if !rest.is_empty() {
            return Err(Error::InvalidDescriptor {
                tag: crate::descriptors::extension::TAG,
                reason: "service_relocated body length must equal 6",
            });
        }
        Ok(ServiceRelocated {
            old_original_network_id: u16::from_be_bytes([hdr[0], hdr[1]]),
            old_transport_stream_id: u16::from_be_bytes([hdr[2], hdr[3]]),
            old_service_id: u16::from_be_bytes([hdr[4], hdr[5]]),
        })
    }
}

impl Serialize for ServiceRelocated {
    type Error = crate::error::Error;
    fn serialized_len(&self) -> usize {
        SERVICE_RELOCATED_LEN
    }
    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
        let len = self.serialized_len();
        need_out(buf, len)?;
        buf[0..2].copy_from_slice(&self.old_original_network_id.to_be_bytes());
        buf[2..4].copy_from_slice(&self.old_transport_stream_id.to_be_bytes());
        buf[4..6].copy_from_slice(&self.old_service_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_service_relocated() {
        let sel = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC];
        let bytes = wrap(0x0B, &sel);
        let d = ExtensionDescriptor::parse(&bytes).unwrap();
        assert_eq!(d.kind(), Some(ExtensionTag::ServiceRelocated));
        match &d.body {
            ExtensionBody::ServiceRelocated(b) => {
                assert_eq!(b.old_original_network_id, 0x1234);
                assert_eq!(b.old_transport_stream_id, 0x5678);
                assert_eq!(b.old_service_id, 0x9ABC);
            }
            other => panic!("expected ServiceRelocated, got {other:?}"),
        }
        round_trip(&d);
    }
}