Skip to main content

dvb_si/descriptors/
data_broadcast.rs

1//! Data Broadcast Descriptor — ETSI EN 300 468 §6.2.12 (tag 0x64).
2//!
3//! Table 31 (PDF p. 71). Identifies a data broadcast component: its
4//! data_broadcast_id, the component_tag tying it to a stream_identifier, a raw
5//! selector tail, plus a localised text description in one language.
6
7use crate::error::{Error, Result};
8use crate::text::{DvbText, LangCode};
9use crate::traits::Descriptor;
10use dvb_common::{Parse, Serialize};
11
12/// Descriptor tag for data_broadcast_descriptor.
13pub const TAG: u8 = 0x64;
14const HEADER_LEN: usize = 2;
15const ID_LEN: usize = 2;
16const COMPONENT_TAG_LEN: usize = 1;
17const SELECTOR_LEN_FIELD: usize = 1;
18const LANG_LEN: usize = 3;
19const TEXT_LEN_FIELD: usize = 1;
20
21/// Data Broadcast Descriptor (tag 0x64).
22#[derive(Debug, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize))] // Deserialize dropped: DvbText is serialize-only
24#[cfg_attr(feature = "serde", serde(bound = ""))]
25pub struct DataBroadcastDescriptor<'a> {
26    /// 16-bit data_broadcast_id (ETSI TS 101 162 registration).
27    pub data_broadcast_id: u16,
28    /// component_tag linking this entry to a stream_identifier_descriptor.
29    pub component_tag: u8,
30    /// Raw selector_byte tail — interpretation depends on data_broadcast_id.
31    #[cfg_attr(feature = "serde", serde(borrow))]
32    pub selector: &'a [u8],
33    /// ISO 639-2 language code of the text description.
34    pub language_code: LangCode,
35    /// DVB Annex-A encoded text description.
36    pub text: DvbText<'a>,
37}
38
39impl<'a> Parse<'a> for DataBroadcastDescriptor<'a> {
40    type Error = crate::error::Error;
41    fn parse(bytes: &'a [u8]) -> Result<Self> {
42        let min_body = ID_LEN + COMPONENT_TAG_LEN + SELECTOR_LEN_FIELD + LANG_LEN + TEXT_LEN_FIELD;
43        if bytes.len() < HEADER_LEN {
44            return Err(Error::BufferTooShort {
45                need: HEADER_LEN,
46                have: bytes.len(),
47                what: "DataBroadcastDescriptor header",
48            });
49        }
50        if bytes[0] != TAG {
51            return Err(Error::InvalidDescriptor {
52                tag: bytes[0],
53                reason: "unexpected tag for data_broadcast_descriptor",
54            });
55        }
56        let length = bytes[1] as usize;
57        let end = HEADER_LEN + length;
58        if bytes.len() < end {
59            return Err(Error::BufferTooShort {
60                need: end,
61                have: bytes.len(),
62                what: "DataBroadcastDescriptor body",
63            });
64        }
65        if length < min_body {
66            return Err(Error::InvalidDescriptor {
67                tag: TAG,
68                reason: "data_broadcast_descriptor body shorter than minimum 8 bytes",
69            });
70        }
71        let mut pos = HEADER_LEN;
72        let data_broadcast_id = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
73        pos += ID_LEN;
74        let component_tag = bytes[pos];
75        pos += COMPONENT_TAG_LEN;
76
77        let selector_length = bytes[pos] as usize;
78        pos += SELECTOR_LEN_FIELD;
79        let selector_end = pos + selector_length;
80        // Need selector + lang(3) + text_length(1) to still fit.
81        if selector_end + LANG_LEN + TEXT_LEN_FIELD > end {
82            return Err(Error::InvalidDescriptor {
83                tag: TAG,
84                reason: "selector_length runs past descriptor end",
85            });
86        }
87        let selector = &bytes[pos..selector_end];
88        pos = selector_end;
89
90        let language_code = LangCode([bytes[pos], bytes[pos + 1], bytes[pos + 2]]);
91        pos += LANG_LEN;
92
93        let text_length = bytes[pos] as usize;
94        pos += TEXT_LEN_FIELD;
95        let text_end = pos + text_length;
96        if text_end > end {
97            return Err(Error::InvalidDescriptor {
98                tag: TAG,
99                reason: "text_length runs past descriptor end",
100            });
101        }
102        let text = DvbText::new(&bytes[pos..text_end]);
103
104        Ok(Self {
105            data_broadcast_id,
106            component_tag,
107            selector,
108            language_code,
109            text,
110        })
111    }
112}
113
114impl Serialize for DataBroadcastDescriptor<'_> {
115    type Error = crate::error::Error;
116    fn serialized_len(&self) -> usize {
117        HEADER_LEN
118            + ID_LEN
119            + COMPONENT_TAG_LEN
120            + SELECTOR_LEN_FIELD
121            + self.selector.len()
122            + LANG_LEN
123            + TEXT_LEN_FIELD
124            + self.text.len()
125    }
126
127    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
128        if self.selector.len() > u8::MAX as usize {
129            return Err(Error::InvalidDescriptor {
130                tag: TAG,
131                reason: "selector exceeds 255 bytes (selector_length is 8-bit)",
132            });
133        }
134        if self.text.len() > u8::MAX as usize {
135            return Err(Error::InvalidDescriptor {
136                tag: TAG,
137                reason: "text exceeds 255 bytes (text_length is 8-bit)",
138            });
139        }
140        let len = self.serialized_len();
141        let body = len - HEADER_LEN;
142        if body > u8::MAX as usize {
143            return Err(Error::InvalidDescriptor {
144                tag: TAG,
145                reason: "data_broadcast_descriptor body exceeds 255 bytes",
146            });
147        }
148        if buf.len() < len {
149            return Err(Error::OutputBufferTooSmall {
150                need: len,
151                have: buf.len(),
152            });
153        }
154        buf[0] = TAG;
155        buf[1] = body as u8;
156        let mut pos = HEADER_LEN;
157        buf[pos..pos + ID_LEN].copy_from_slice(&self.data_broadcast_id.to_be_bytes());
158        pos += ID_LEN;
159        buf[pos] = self.component_tag;
160        pos += COMPONENT_TAG_LEN;
161        buf[pos] = self.selector.len() as u8;
162        pos += SELECTOR_LEN_FIELD;
163        buf[pos..pos + self.selector.len()].copy_from_slice(self.selector);
164        pos += self.selector.len();
165        buf[pos..pos + LANG_LEN].copy_from_slice(&self.language_code.0);
166        pos += LANG_LEN;
167        buf[pos] = self.text.len() as u8;
168        pos += TEXT_LEN_FIELD;
169        buf[pos..pos + self.text.len()].copy_from_slice(self.text.raw());
170        Ok(len)
171    }
172}
173
174impl<'a> Descriptor<'a> for DataBroadcastDescriptor<'a> {
175    const TAG: u8 = TAG;
176    fn descriptor_length(&self) -> u8 {
177        (self.serialized_len() - HEADER_LEN) as u8
178    }
179}
180
181impl<'a> crate::traits::DescriptorDef<'a> for DataBroadcastDescriptor<'a> {
182    const TAG: u8 = TAG;
183    const NAME: &'static str = "DATA_BROADCAST";
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    fn build(id: u16, ctag: u8, selector: &[u8], lang: [u8; 3], text: &[u8]) -> Vec<u8> {
191        let body = ID_LEN
192            + COMPONENT_TAG_LEN
193            + SELECTOR_LEN_FIELD
194            + selector.len()
195            + LANG_LEN
196            + TEXT_LEN_FIELD
197            + text.len();
198        let mut v = Vec::with_capacity(HEADER_LEN + body);
199        v.push(TAG);
200        v.push(body as u8);
201        v.extend_from_slice(&id.to_be_bytes());
202        v.push(ctag);
203        v.push(selector.len() as u8);
204        v.extend_from_slice(selector);
205        v.extend_from_slice(&lang);
206        v.push(text.len() as u8);
207        v.extend_from_slice(text);
208        v
209    }
210
211    #[test]
212    fn parse_extracts_all_fields() {
213        let bytes = build(0x000B, 0x12, &[0xAA, 0xBB], *b"eng", b"Hello");
214        let d = DataBroadcastDescriptor::parse(&bytes).unwrap();
215        assert_eq!(d.data_broadcast_id, 0x000B);
216        assert_eq!(d.component_tag, 0x12);
217        assert_eq!(d.selector, &[0xAA, 0xBB]);
218        assert_eq!(d.language_code, LangCode(*b"eng"));
219        assert_eq!(d.text.raw(), b"Hello");
220    }
221
222    #[test]
223    fn parse_accepts_empty_selector_and_text() {
224        let bytes = build(0x0001, 0x00, &[], *b"fra", b"");
225        let d = DataBroadcastDescriptor::parse(&bytes).unwrap();
226        assert!(d.selector.is_empty());
227        assert!(d.text.raw().is_empty());
228        assert_eq!(d.language_code, LangCode(*b"fra"));
229    }
230
231    #[test]
232    fn parse_rejects_wrong_tag() {
233        let mut bytes = build(0x0001, 0x00, &[], *b"eng", b"");
234        bytes[0] = 0x65;
235        let err = DataBroadcastDescriptor::parse(&bytes).unwrap_err();
236        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x65, .. }));
237    }
238
239    #[test]
240    fn parse_rejects_short_buffer() {
241        let err = DataBroadcastDescriptor::parse(&[TAG]).unwrap_err();
242        assert!(matches!(err, Error::BufferTooShort { .. }));
243    }
244
245    #[test]
246    fn parse_rejects_body_too_short() {
247        // length=4: cannot hold the 8-byte minimum.
248        let err = DataBroadcastDescriptor::parse(&[TAG, 4, 0, 0, 0, 0]).unwrap_err();
249        assert!(matches!(err, Error::InvalidDescriptor { .. }));
250    }
251
252    #[test]
253    fn parse_rejects_selector_length_overrun() {
254        // selector_length=200 but body is tiny.
255        let bytes = [TAG, 8, 0x00, 0x0B, 0x12, 200, b'e', b'n', b'g', 0];
256        let err = DataBroadcastDescriptor::parse(&bytes).unwrap_err();
257        assert!(matches!(err, Error::InvalidDescriptor { .. }));
258    }
259
260    #[test]
261    fn parse_rejects_text_length_overrun() {
262        // selector_length=0, lang present, text_length=5 but no text bytes.
263        let bytes = [TAG, 8, 0x00, 0x0B, 0x12, 0, b'e', b'n', b'g', 5];
264        let err = DataBroadcastDescriptor::parse(&bytes).unwrap_err();
265        assert!(matches!(err, Error::InvalidDescriptor { .. }));
266    }
267
268    #[test]
269    fn serialize_round_trip() {
270        let bytes = build(0x0123, 0x45, &[0xDE, 0xAD], *b"deu", b"Daten");
271        let parsed = DataBroadcastDescriptor::parse(&bytes).unwrap();
272        let mut buf = vec![0u8; parsed.serialized_len()];
273        parsed.serialize_into(&mut buf).unwrap();
274        assert_eq!(buf, bytes);
275        let re = DataBroadcastDescriptor::parse(&buf).unwrap();
276        assert_eq!(parsed, re);
277    }
278
279    #[test]
280    fn serialize_rejects_too_small_buffer() {
281        let d = DataBroadcastDescriptor {
282            data_broadcast_id: 0x0001,
283            component_tag: 0x00,
284            selector: &[],
285            language_code: LangCode(*b"eng"),
286            text: DvbText::new(&[]),
287        };
288        let mut tiny = [0u8; 4];
289        let err = d.serialize_into(&mut tiny).unwrap_err();
290        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
291    }
292
293    #[test]
294    fn serialize_rejects_over_range_selector() {
295        let sel = vec![0u8; 256];
296        let d = DataBroadcastDescriptor {
297            data_broadcast_id: 0x0001,
298            component_tag: 0x00,
299            selector: &sel,
300            language_code: LangCode(*b"eng"),
301            text: DvbText::new(&[]),
302        };
303        let mut buf = vec![0u8; d.serialized_len()];
304        let err = d.serialize_into(&mut buf).unwrap_err();
305        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
306    }
307
308    #[test]
309    fn serialize_rejects_over_range_body() {
310        // selector 250 + text 10 + fixed 7 = 267 > 255, both sub-fields in range.
311        let sel = vec![0u8; 250];
312        let txt = vec![0u8; 10];
313        let d = DataBroadcastDescriptor {
314            data_broadcast_id: 0x0001,
315            component_tag: 0x00,
316            selector: &sel,
317            language_code: LangCode(*b"eng"),
318            text: DvbText::new(&txt),
319        };
320        let mut buf = vec![0u8; d.serialized_len()];
321        let err = d.serialize_into(&mut buf).unwrap_err();
322        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
323    }
324
325    #[cfg(feature = "serde")]
326    #[test]
327    fn serde_serialize_is_stable() {
328        // Borrowed `&[u8]` fields cannot be deserialized from a JSON array by
329        // serde_json (it cannot borrow out of an owned String), so — matching
330        // the borrowed-bytes descriptors in this crate (linkage, short_event)
331        // — we exercise the serialize path and assert it is deterministic.
332        let d = DataBroadcastDescriptor {
333            data_broadcast_id: 0x000B,
334            component_tag: 0x09,
335            selector: &[0x01, 0x02],
336            language_code: LangCode(*b"eng"),
337            text: DvbText::new(b"Text"),
338        };
339        let json = serde_json::to_string(&d).unwrap();
340        assert_eq!(json, serde_json::to_string(&d.clone()).unwrap());
341    }
342}