Skip to main content

dvb_si/descriptors/
vbi_data.rs

1//! VBI Data Descriptor โ€” ETSI EN 300 468 ยง6.2.47 (tag 0x45).
2//!
3//! Table 106 (PDF p. 110). Carried in PMT ES_info for an elementary stream
4//! carrying VBI data (per ETSI EN 301 775). Body is a loop of entries, each a
5//! `data_service_id` byte + an 8-bit `data_service_descriptor_length` + that
6//! many service-descriptor bytes.
7//!
8//! The first loop level (data_service_id + length-delimited service block) is
9//! typed. The inner per-line content (each byte either
10//! `reserved(2)|field_parity(1)|line_offset(5)` for data_service_id โˆˆ
11//! {0x01,0x02,0x04,0x05,0x06,0x07}, or 8 reserved bits otherwise) is kept raw
12//! as a borrowed `&[u8]` per house convention โ€” its meaning is selected by
13//! `data_service_id`, so per-byte typing would be fragile (Table 106/107).
14
15use crate::error::{Error, Result};
16use crate::traits::Descriptor;
17use dvb_common::{Parse, Serialize};
18
19/// Descriptor tag for VBI_data_descriptor.
20pub const TAG: u8 = 0x45;
21const HEADER_LEN: usize = 2;
22const ENTRY_HEADER_LEN: usize = 2; // data_service_id + data_service_descriptor_length
23/// Maximum body length expressible in the 8-bit `descriptor_length` field.
24const MAX_BODY_LEN: usize = u8::MAX as usize;
25/// Maximum per-entry service block length (8-bit length field).
26const MAX_SERVICE_LEN: usize = u8::MAX as usize;
27
28/// One VBI data service entry.
29#[derive(Debug, Clone, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
32pub struct VbiDataEntry<'a> {
33    /// data_service_id (EN 300 468 Table 107): 0x01 = EBU teletext,
34    /// 0x02 = inverted teletext, 0x04 = VPS, 0x05 = WSS, 0x06 = closed
35    /// captioning, 0x07 = monochrome 4:2:2 samples; others reserved/user.
36    pub data_service_id: u8,
37    /// Raw service-descriptor bytes (one byte per VBI line; layout selected by
38    /// `data_service_id` per Table 106). Kept opaque per house convention.
39    pub service_descriptor: &'a [u8],
40}
41
42/// VBI Data Descriptor.
43#[derive(Debug, Clone, PartialEq, Eq)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize))]
45#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
46pub struct VbiDataDescriptor<'a> {
47    /// Service entries in wire order.
48    pub entries: Vec<VbiDataEntry<'a>>,
49}
50
51impl<'a> Parse<'a> for VbiDataDescriptor<'a> {
52    type Error = crate::error::Error;
53    fn parse(bytes: &'a [u8]) -> Result<Self> {
54        if bytes.len() < HEADER_LEN {
55            return Err(Error::BufferTooShort {
56                need: HEADER_LEN,
57                have: bytes.len(),
58                what: "VbiDataDescriptor header",
59            });
60        }
61        if bytes[0] != TAG {
62            return Err(Error::InvalidDescriptor {
63                tag: bytes[0],
64                reason: "unexpected tag for VBI_data_descriptor",
65            });
66        }
67        let length = bytes[1] as usize;
68        let end = HEADER_LEN + length;
69        if bytes.len() < end {
70            return Err(Error::BufferTooShort {
71                need: end,
72                have: bytes.len(),
73                what: "VbiDataDescriptor body",
74            });
75        }
76        let body = &bytes[HEADER_LEN..end];
77        let mut entries = Vec::new();
78        let mut pos = 0;
79        while pos < body.len() {
80            if pos + ENTRY_HEADER_LEN > body.len() {
81                return Err(Error::InvalidDescriptor {
82                    tag: TAG,
83                    reason: "truncated VBI data entry header",
84                });
85            }
86            let data_service_id = body[pos];
87            let svc_len = body[pos + 1] as usize;
88            pos += ENTRY_HEADER_LEN;
89            if pos + svc_len > body.len() {
90                return Err(Error::InvalidDescriptor {
91                    tag: TAG,
92                    reason: "data_service_descriptor_length exceeds descriptor body",
93                });
94            }
95            let service_descriptor = &body[pos..pos + svc_len];
96            pos += svc_len;
97            entries.push(VbiDataEntry {
98                data_service_id,
99                service_descriptor,
100            });
101        }
102        Ok(Self { entries })
103    }
104}
105
106impl Serialize for VbiDataDescriptor<'_> {
107    type Error = crate::error::Error;
108    fn serialized_len(&self) -> usize {
109        HEADER_LEN
110            + self
111                .entries
112                .iter()
113                .map(|e| ENTRY_HEADER_LEN + e.service_descriptor.len())
114                .sum::<usize>()
115    }
116
117    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
118        let len = self.serialized_len();
119        if buf.len() < len {
120            return Err(Error::OutputBufferTooSmall {
121                need: len,
122                have: buf.len(),
123            });
124        }
125        let body_len = len - HEADER_LEN;
126        // 8-bit descriptor_length field: error rather than silently truncate.
127        if body_len > MAX_BODY_LEN {
128            return Err(Error::SectionLengthOverflow {
129                declared: body_len,
130                available: MAX_BODY_LEN,
131            });
132        }
133        buf[0] = TAG;
134        buf[1] = body_len as u8;
135        let mut pos = HEADER_LEN;
136        for e in &self.entries {
137            // 8-bit data_service_descriptor_length field: error on over-range.
138            if e.service_descriptor.len() > MAX_SERVICE_LEN {
139                return Err(Error::InvalidDescriptor {
140                    tag: TAG,
141                    reason: "service_descriptor exceeds 255 bytes (8-bit length field)",
142                });
143            }
144            buf[pos] = e.data_service_id;
145            buf[pos + 1] = e.service_descriptor.len() as u8;
146            pos += ENTRY_HEADER_LEN;
147            buf[pos..pos + e.service_descriptor.len()].copy_from_slice(e.service_descriptor);
148            pos += e.service_descriptor.len();
149        }
150        Ok(len)
151    }
152}
153
154impl<'a> Descriptor<'a> for VbiDataDescriptor<'a> {
155    const TAG: u8 = TAG;
156    fn descriptor_length(&self) -> u8 {
157        (self.serialized_len() - HEADER_LEN) as u8
158    }
159}
160
161impl<'a> crate::traits::DescriptorDef<'a> for VbiDataDescriptor<'a> {
162    const TAG: u8 = TAG;
163    const NAME: &'static str = "VBI_DATA";
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn parse_single_entry() {
172        // data_service_id=0x01 (EBU teletext), 2 line bytes
173        let bytes = [TAG, 4, 0x01, 0x02, 0xC1, 0xC2];
174        let d = VbiDataDescriptor::parse(&bytes).unwrap();
175        assert_eq!(d.entries.len(), 1);
176        assert_eq!(d.entries[0].data_service_id, 0x01);
177        assert_eq!(d.entries[0].service_descriptor, &[0xC1, 0xC2]);
178    }
179
180    #[test]
181    fn parse_multiple_entries() {
182        let bytes = [TAG, 7, 0x04, 0x01, 0xAA, 0x05, 0x02, 0xBB, 0xCC];
183        let d = VbiDataDescriptor::parse(&bytes).unwrap();
184        assert_eq!(d.entries.len(), 2);
185        assert_eq!(d.entries[0].data_service_id, 0x04);
186        assert_eq!(d.entries[0].service_descriptor, &[0xAA]);
187        assert_eq!(d.entries[1].data_service_id, 0x05);
188        assert_eq!(d.entries[1].service_descriptor, &[0xBB, 0xCC]);
189    }
190
191    #[test]
192    fn parse_entry_with_empty_service_block() {
193        let bytes = [TAG, 2, 0x06, 0x00];
194        let d = VbiDataDescriptor::parse(&bytes).unwrap();
195        assert_eq!(d.entries.len(), 1);
196        assert!(d.entries[0].service_descriptor.is_empty());
197    }
198
199    #[test]
200    fn parse_rejects_wrong_tag() {
201        assert!(matches!(
202            VbiDataDescriptor::parse(&[0x46, 0]).unwrap_err(),
203            Error::InvalidDescriptor { tag: 0x46, .. }
204        ));
205    }
206
207    #[test]
208    fn parse_rejects_short_buffer() {
209        // declares 4 body bytes, only 2 present
210        let bytes = [TAG, 4, 0x01, 0x02];
211        assert!(matches!(
212            VbiDataDescriptor::parse(&bytes).unwrap_err(),
213            Error::BufferTooShort { .. }
214        ));
215    }
216
217    #[test]
218    fn parse_rejects_inner_length_overrun() {
219        // entry declares 5 service bytes but only 1 remains in body
220        let bytes = [TAG, 3, 0x01, 0x05, 0xAA];
221        assert!(matches!(
222            VbiDataDescriptor::parse(&bytes).unwrap_err(),
223            Error::InvalidDescriptor { tag: TAG, .. }
224        ));
225    }
226
227    #[test]
228    fn empty_descriptor_valid() {
229        let d = VbiDataDescriptor::parse(&[TAG, 0]).unwrap();
230        assert!(d.entries.is_empty());
231    }
232
233    #[test]
234    fn serialize_round_trip() {
235        let d = VbiDataDescriptor {
236            entries: vec![
237                VbiDataEntry {
238                    data_service_id: 0x01,
239                    service_descriptor: &[0xC1, 0xC2, 0xC3],
240                },
241                VbiDataEntry {
242                    data_service_id: 0x04,
243                    service_descriptor: &[],
244                },
245            ],
246        };
247        let mut buf = vec![0u8; d.serialized_len()];
248        d.serialize_into(&mut buf).unwrap();
249        assert_eq!(VbiDataDescriptor::parse(&buf).unwrap(), d);
250    }
251
252    #[test]
253    fn serialize_rejects_small_buffer() {
254        let d = VbiDataDescriptor {
255            entries: vec![VbiDataEntry {
256                data_service_id: 0x01,
257                service_descriptor: &[0xAA],
258            }],
259        };
260        let mut tiny = [0u8; 3];
261        assert!(matches!(
262            d.serialize_into(&mut tiny).unwrap_err(),
263            Error::OutputBufferTooSmall { .. }
264        ));
265    }
266
267    #[cfg(feature = "serde")]
268    #[test]
269    fn serde_serialize_stable() {
270        // Borrowed-byte fields cannot deserialize from a JSON array (serde_json
271        // requires a borrowed-str for &[u8]); assert the Serialize half is
272        // stable, matching the other borrowed descriptors (e.g.
273        // content_identifier) in this crate.
274        let make = || VbiDataDescriptor {
275            entries: vec![VbiDataEntry {
276                data_service_id: 0x01,
277                service_descriptor: &[0xC1, 0xC2],
278            }],
279        };
280        let json = serde_json::to_string(&make()).unwrap();
281        assert!(json.contains("data_service_id"));
282        assert_eq!(json, serde_json::to_string(&make()).unwrap());
283    }
284}