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