Skip to main content

dvb_si/descriptors/
linkage.rs

1//! Linkage Descriptor — ETSI EN 300 468 §6.2.19 (tag 0x4A).
2//!
3//! Carried inside NIT (network linkage), BAT (bouquet linkage) and
4//! SDT (service replacement / premiere hand-over).
5
6use crate::error::{Error, Result};
7use crate::traits::Descriptor;
8use dvb_common::{Parse, Serialize};
9
10/// Descriptor tag for linkage_descriptor.
11pub const TAG: u8 = 0x4A;
12const HEADER_LEN: usize = 2;
13const FIXED_FIELDS_LEN: usize = 7;
14
15/// Linkage Descriptor.
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct LinkageDescriptor<'a> {
19    /// transport_stream_id of the linked-to TS.
20    pub transport_stream_id: u16,
21    /// original_network_id of the linked-to TS.
22    pub original_network_id: u16,
23    /// service_id of the linked-to service (0 if linkage is at the network or
24    /// bouquet level).
25    pub service_id: u16,
26    /// linkage_type byte (ETSI Table 70): 0x01 information, 0x02 EPG,
27    /// 0x03 CA_replacement, 0x04 TS_containing_complete_SI, 0x05 service_replacement,
28    /// 0x06 data_broadcast, 0x07 RCS_map, 0x08 mobile_hand-over, etc.
29    pub linkage_type: u8,
30    /// Raw private_data_byte tail — interpretation depends on linkage_type.
31    #[cfg_attr(feature = "serde", serde(borrow))]
32    pub private_data: &'a [u8],
33}
34
35impl<'a> Parse<'a> for LinkageDescriptor<'a> {
36    type Error = crate::error::Error;
37    fn parse(bytes: &'a [u8]) -> Result<Self> {
38        if bytes.len() < HEADER_LEN {
39            return Err(Error::BufferTooShort {
40                need: HEADER_LEN,
41                have: bytes.len(),
42                what: "LinkageDescriptor header",
43            });
44        }
45        if bytes[0] != TAG {
46            return Err(Error::InvalidDescriptor {
47                tag: bytes[0],
48                reason: "unexpected tag for linkage_descriptor",
49            });
50        }
51        let length = bytes[1] as usize;
52        let end = HEADER_LEN + length;
53        if bytes.len() < end {
54            return Err(Error::BufferTooShort {
55                need: end,
56                have: bytes.len(),
57                what: "LinkageDescriptor body",
58            });
59        }
60        if length < FIXED_FIELDS_LEN {
61            return Err(Error::InvalidDescriptor {
62                tag: TAG,
63                reason: "linkage_descriptor body shorter than minimum 7 bytes",
64            });
65        }
66        let body_start = HEADER_LEN;
67        let transport_stream_id = u16::from_be_bytes([bytes[body_start], bytes[body_start + 1]]);
68        let original_network_id =
69            u16::from_be_bytes([bytes[body_start + 2], bytes[body_start + 3]]);
70        let service_id = u16::from_be_bytes([bytes[body_start + 4], bytes[body_start + 5]]);
71        let linkage_type = bytes[body_start + 6];
72        let private_data = &bytes[body_start + FIXED_FIELDS_LEN..end];
73        Ok(Self {
74            transport_stream_id,
75            original_network_id,
76            service_id,
77            linkage_type,
78            private_data,
79        })
80    }
81}
82
83impl Serialize for LinkageDescriptor<'_> {
84    type Error = crate::error::Error;
85    fn serialized_len(&self) -> usize {
86        HEADER_LEN + FIXED_FIELDS_LEN + self.private_data.len()
87    }
88
89    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
90        let len = self.serialized_len();
91        if buf.len() < len {
92            return Err(Error::OutputBufferTooSmall {
93                need: len,
94                have: buf.len(),
95            });
96        }
97        buf[0] = TAG;
98        buf[1] = (FIXED_FIELDS_LEN + self.private_data.len()) as u8;
99        let body_start = HEADER_LEN;
100        buf[body_start..body_start + 2].copy_from_slice(&self.transport_stream_id.to_be_bytes());
101        buf[body_start + 2..body_start + 4]
102            .copy_from_slice(&self.original_network_id.to_be_bytes());
103        buf[body_start + 4..body_start + 6].copy_from_slice(&self.service_id.to_be_bytes());
104        buf[body_start + 6] = self.linkage_type;
105        if !self.private_data.is_empty() {
106            let private_start = body_start + FIXED_FIELDS_LEN;
107            buf[private_start..private_start + self.private_data.len()]
108                .copy_from_slice(self.private_data);
109        }
110        Ok(len)
111    }
112}
113
114impl<'a> Descriptor<'a> for LinkageDescriptor<'a> {
115    const TAG: u8 = TAG;
116
117    fn descriptor_length(&self) -> u8 {
118        (FIXED_FIELDS_LEN + self.private_data.len()) as u8
119    }
120}
121
122impl<'a> crate::traits::DescriptorDef<'a> for LinkageDescriptor<'a> {
123    const TAG: u8 = TAG;
124    const NAME: &'static str = "LINKAGE";
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn parse_extracts_tsid_onid_sid() {
133        let bytes = [
134            TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB,
135        ];
136        let d = LinkageDescriptor::parse(&bytes).unwrap();
137        assert_eq!(d.transport_stream_id, 0x0001);
138        assert_eq!(d.original_network_id, 0x0002);
139        assert_eq!(d.service_id, 0x0003);
140    }
141
142    #[test]
143    fn parse_extracts_linkage_type() {
144        let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x06];
145        let d = LinkageDescriptor::parse(&bytes).unwrap();
146        assert_eq!(d.linkage_type, 0x06);
147    }
148
149    #[test]
150    fn parse_preserves_raw_private_data() {
151        let bytes = [
152            TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB, 0xCC,
153        ];
154        let d = LinkageDescriptor::parse(&bytes).unwrap();
155        assert_eq!(d.private_data, &[0xAA, 0xBB, 0xCC]);
156    }
157
158    #[test]
159    fn parse_accepts_empty_private_data() {
160        let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05];
161        let d = LinkageDescriptor::parse(&bytes).unwrap();
162        assert!(d.private_data.is_empty());
163    }
164
165    #[test]
166    fn parse_rejects_wrong_tag() {
167        let err = LinkageDescriptor::parse(&[0x4B, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05])
168            .unwrap_err();
169        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x4B, .. }));
170    }
171
172    #[test]
173    fn parse_rejects_body_shorter_than_seven() {
174        let bytes = [TAG, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00];
175        let err = LinkageDescriptor::parse(&bytes).unwrap_err();
176        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
177    }
178
179    #[test]
180    fn parse_rejects_truncated_buffer() {
181        let err = LinkageDescriptor::parse(&[TAG]).unwrap_err();
182        assert!(matches!(err, Error::BufferTooShort { .. }));
183    }
184
185    #[test]
186    fn serialize_round_trip_no_private_data() {
187        let d = LinkageDescriptor {
188            transport_stream_id: 0x1234,
189            original_network_id: 0x5678,
190            service_id: 0xABCD,
191            linkage_type: 0x02,
192            private_data: &[],
193        };
194        let mut buf = vec![0u8; d.serialized_len()];
195        d.serialize_into(&mut buf).unwrap();
196        let re = LinkageDescriptor::parse(&buf).unwrap();
197        assert_eq!(d, re);
198    }
199
200    #[test]
201    fn serialize_round_trip_with_private_data() {
202        let private = [0xDE, 0xAD, 0xBE, 0xEF];
203        let d = LinkageDescriptor {
204            transport_stream_id: 0x0001,
205            original_network_id: 0x0002,
206            service_id: 0x0003,
207            linkage_type: 0x05,
208            private_data: &private,
209        };
210        let mut buf = vec![0u8; d.serialized_len()];
211        d.serialize_into(&mut buf).unwrap();
212        let re = LinkageDescriptor::parse(&buf).unwrap();
213        assert_eq!(d, re);
214    }
215}