1use super::descriptor_body;
14use crate::error::{Error, Result};
15use alloc::vec::Vec;
16use dvb_common::{Parse, Serialize};
17
18pub const TAG: u8 = 0x45;
20const HEADER_LEN: usize = 2;
21const ENTRY_HEADER_LEN: usize = 2;
22const MAX_BODY_LEN: usize = u8::MAX as usize;
23const MAX_SERVICE_LEN: usize = u8::MAX as usize;
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize))]
28#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
29#[non_exhaustive]
30pub enum VbiService<'a> {
31 Lines(Vec<VbiLine>),
34 Reserved(&'a [u8]),
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize))]
41pub struct VbiLine {
42 pub field_parity: bool,
44 pub line_offset: u8,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize))]
51#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
52pub struct VbiDataEntry<'a> {
53 pub data_service_id: u8,
57 pub service_descriptor: VbiService<'a>,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize))]
64#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
65pub struct VbiDataDescriptor<'a> {
66 pub entries: Vec<VbiDataEntry<'a>>,
68}
69
70impl<'a> Parse<'a> for VbiDataDescriptor<'a> {
71 type Error = crate::error::Error;
72 fn parse(bytes: &'a [u8]) -> Result<Self> {
73 let body = descriptor_body(
74 bytes,
75 TAG,
76 "VbiDataDescriptor",
77 "unexpected tag for VBI_data_descriptor",
78 )?;
79 let mut entries = Vec::new();
80 let mut pos = 0;
81 while pos < body.len() {
82 if pos + ENTRY_HEADER_LEN > body.len() {
83 return Err(Error::InvalidDescriptor {
84 tag: TAG,
85 reason: "truncated VBI data entry header",
86 });
87 }
88 let data_service_id = body[pos];
89 let svc_len = body[pos + 1] as usize;
90 pos += ENTRY_HEADER_LEN;
91 if pos + svc_len > body.len() {
92 return Err(Error::InvalidDescriptor {
93 tag: TAG,
94 reason: "data_service_descriptor_length exceeds descriptor body",
95 });
96 }
97 let svc_bytes = &body[pos..pos + svc_len];
98 pos += svc_len;
99 let service_descriptor = if matches!(data_service_id, 0x01 | 0x02 | 0x04..=0x07) {
100 let lines = svc_bytes
101 .iter()
102 .map(|&b| VbiLine {
103 field_parity: (b & 0x20) != 0,
104 line_offset: b & 0x1F,
105 })
106 .collect();
107 VbiService::Lines(lines)
108 } else {
109 VbiService::Reserved(svc_bytes)
110 };
111 entries.push(VbiDataEntry {
112 data_service_id,
113 service_descriptor,
114 });
115 }
116 Ok(Self { entries })
117 }
118}
119
120impl Serialize for VbiDataDescriptor<'_> {
121 type Error = crate::error::Error;
122 fn serialized_len(&self) -> usize {
123 HEADER_LEN
124 + self
125 .entries
126 .iter()
127 .map(|e| {
128 ENTRY_HEADER_LEN
129 + match &e.service_descriptor {
130 VbiService::Lines(lines) => lines.len(),
131 VbiService::Reserved(b) => b.len(),
132 }
133 })
134 .sum::<usize>()
135 }
136
137 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
138 let len = self.serialized_len();
139 if buf.len() < len {
140 return Err(Error::OutputBufferTooSmall {
141 need: len,
142 have: buf.len(),
143 });
144 }
145 let body_len = len - HEADER_LEN;
146 if body_len > MAX_BODY_LEN {
147 return Err(Error::InvalidDescriptor {
148 tag: TAG,
149 reason: "VBI_data_descriptor body exceeds 255 bytes",
150 });
151 }
152 buf[0] = TAG;
153 buf[1] = body_len as u8;
154 let mut pos = HEADER_LEN;
155 for e in &self.entries {
156 let svc_len = match &e.service_descriptor {
157 VbiService::Lines(lines) => lines.len(),
158 VbiService::Reserved(b) => b.len(),
159 };
160 if svc_len > MAX_SERVICE_LEN {
161 return Err(Error::InvalidDescriptor {
162 tag: TAG,
163 reason: "service_descriptor exceeds 255 bytes (8-bit length field)",
164 });
165 }
166 buf[pos] = e.data_service_id;
167 buf[pos + 1] = svc_len as u8;
168 pos += ENTRY_HEADER_LEN;
169 match &e.service_descriptor {
170 VbiService::Lines(lines) => {
171 for line in lines {
172 buf[pos] =
173 0xC0 | (u8::from(line.field_parity) << 5) | (line.line_offset & 0x1F);
174 pos += 1;
175 }
176 }
177 VbiService::Reserved(b) => {
178 buf[pos..pos + b.len()].copy_from_slice(b);
179 pos += b.len();
180 }
181 }
182 }
183 Ok(len)
184 }
185}
186impl<'a> crate::traits::DescriptorDef<'a> for VbiDataDescriptor<'a> {
187 const TAG: u8 = TAG;
188 const NAME: &'static str = "VBI_DATA";
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn parse_single_entry() {
197 let bytes = [TAG, 4, 0x01, 0x02, 0xC1, 0xC2];
201 let d = VbiDataDescriptor::parse(&bytes).unwrap();
202 assert_eq!(d.entries.len(), 1);
203 assert_eq!(d.entries[0].data_service_id, 0x01);
204 match &d.entries[0].service_descriptor {
205 VbiService::Lines(lines) => {
206 assert_eq!(lines.len(), 2);
207 assert!(!lines[0].field_parity);
208 assert_eq!(lines[0].line_offset, 0x01);
209 assert!(!lines[1].field_parity);
210 assert_eq!(lines[1].line_offset, 0x02);
211 }
212 other => panic!("expected Lines, got {other:?}"),
213 }
214 }
215
216 #[test]
217 fn parse_multiple_entries() {
218 let bytes = [TAG, 7, 0x04, 0x01, 0xAA, 0x05, 0x02, 0xBB, 0xCC];
222 let d = VbiDataDescriptor::parse(&bytes).unwrap();
223 assert_eq!(d.entries.len(), 2);
224 assert_eq!(d.entries[0].data_service_id, 0x04);
225 match &d.entries[0].service_descriptor {
226 VbiService::Lines(lines) => {
227 assert_eq!(lines.len(), 1);
228 assert!(lines[0].field_parity);
229 assert_eq!(lines[0].line_offset, 0x0A);
230 }
231 other => panic!("expected Lines, got {other:?}"),
232 }
233 assert_eq!(d.entries[1].data_service_id, 0x05);
234 match &d.entries[1].service_descriptor {
235 VbiService::Lines(lines) => {
236 assert_eq!(lines.len(), 2);
237 assert!(lines[0].field_parity);
238 assert_eq!(lines[0].line_offset, 0x1B);
239 assert!(!lines[1].field_parity);
240 assert_eq!(lines[1].line_offset, 0x0C);
241 }
242 other => panic!("expected Lines, got {other:?}"),
243 }
244 }
245
246 #[test]
247 fn parse_entry_with_empty_service_block() {
248 let bytes = [TAG, 2, 0x06, 0x00];
249 let d = VbiDataDescriptor::parse(&bytes).unwrap();
250 assert_eq!(d.entries.len(), 1);
251 match &d.entries[0].service_descriptor {
252 VbiService::Lines(lines) => assert!(lines.is_empty()),
253 other => panic!("expected Lines, got {other:?}"),
254 }
255 }
256
257 #[test]
258 fn parse_reserved_data_service_id() {
259 let bytes = [TAG, 4, 0x00, 0x02, 0xDE, 0xAD];
260 let d = VbiDataDescriptor::parse(&bytes).unwrap();
261 assert_eq!(d.entries.len(), 1);
262 assert_eq!(d.entries[0].data_service_id, 0x00);
263 match &d.entries[0].service_descriptor {
264 VbiService::Reserved(b) => assert_eq!(*b, &[0xDE, 0xAD]),
265 other => panic!("expected Reserved, got {other:?}"),
266 }
267 }
268
269 #[test]
270 fn parse_rejects_wrong_tag() {
271 assert!(matches!(
272 VbiDataDescriptor::parse(&[0x46, 0]).unwrap_err(),
273 Error::InvalidDescriptor { tag: 0x46, .. }
274 ));
275 }
276
277 #[test]
278 fn parse_rejects_short_buffer() {
279 let bytes = [TAG, 4, 0x01, 0x02];
281 assert!(matches!(
282 VbiDataDescriptor::parse(&bytes).unwrap_err(),
283 Error::BufferTooShort { .. }
284 ));
285 }
286
287 #[test]
288 fn parse_rejects_inner_length_overrun() {
289 let bytes = [TAG, 3, 0x01, 0x05, 0xAA];
291 assert!(matches!(
292 VbiDataDescriptor::parse(&bytes).unwrap_err(),
293 Error::InvalidDescriptor { tag: TAG, .. }
294 ));
295 }
296
297 #[test]
298 fn empty_descriptor_valid() {
299 let d = VbiDataDescriptor::parse(&[TAG, 0]).unwrap();
300 assert!(d.entries.is_empty());
301 }
302
303 #[test]
304 fn serialize_round_trip() {
305 let d = VbiDataDescriptor {
306 entries: vec![
307 VbiDataEntry {
308 data_service_id: 0x01,
309 service_descriptor: VbiService::Lines(vec![
310 VbiLine {
311 field_parity: false,
312 line_offset: 0x01,
313 },
314 VbiLine {
315 field_parity: false,
316 line_offset: 0x02,
317 },
318 VbiLine {
319 field_parity: false,
320 line_offset: 0x03,
321 },
322 ]),
323 },
324 VbiDataEntry {
325 data_service_id: 0x04,
326 service_descriptor: VbiService::Lines(vec![]),
327 },
328 ],
329 };
330 let mut buf = vec![0u8; d.serialized_len()];
331 d.serialize_into(&mut buf).unwrap();
332 assert_eq!(VbiDataDescriptor::parse(&buf).unwrap(), d);
333 }
334
335 #[test]
336 fn serialize_round_trip_reserved() {
337 let d = VbiDataDescriptor {
338 entries: vec![VbiDataEntry {
339 data_service_id: 0x80,
340 service_descriptor: VbiService::Reserved(&[0xDE, 0xAD]),
341 }],
342 };
343 let mut buf = vec![0u8; d.serialized_len()];
344 d.serialize_into(&mut buf).unwrap();
345 assert_eq!(VbiDataDescriptor::parse(&buf).unwrap(), d);
346 }
347
348 #[test]
349 fn byte_identity_with_reserved_bits() {
350 let bytes = [TAG, 4, 0x01, 0x02, 0xC1, 0xC2];
351 let d = VbiDataDescriptor::parse(&bytes).unwrap();
352 let mut buf = vec![0u8; d.serialized_len()];
353 d.serialize_into(&mut buf).unwrap();
354 assert_eq!(buf, bytes);
355 }
356
357 #[test]
358 fn data_service_id_0x03_is_reserved() {
359 let bytes = [TAG, 3, 0x03, 0x01, 0xAA];
360 let d = VbiDataDescriptor::parse(&bytes).unwrap();
361 assert_eq!(d.entries[0].data_service_id, 0x03);
362 match &d.entries[0].service_descriptor {
363 VbiService::Reserved(b) => assert_eq!(*b, &[0xAA]),
364 other => panic!("expected Reserved for id 0x03, got {other:?}"),
365 }
366 }
367
368 #[test]
369 fn serialize_rejects_small_buffer() {
370 let d = VbiDataDescriptor {
371 entries: vec![VbiDataEntry {
372 data_service_id: 0x01,
373 service_descriptor: VbiService::Lines(vec![VbiLine {
374 field_parity: false,
375 line_offset: 0x0A,
376 }]),
377 }],
378 };
379 let mut tiny = [0u8; 3];
380 assert!(matches!(
381 d.serialize_into(&mut tiny).unwrap_err(),
382 Error::OutputBufferTooSmall { .. }
383 ));
384 }
385
386 #[cfg(feature = "serde")]
387 #[test]
388 fn serde_serialize_stable() {
389 let make = || VbiDataDescriptor {
394 entries: vec![VbiDataEntry {
395 data_service_id: 0x01,
396 service_descriptor: VbiService::Lines(vec![
397 VbiLine {
398 field_parity: false,
399 line_offset: 0x01,
400 },
401 VbiLine {
402 field_parity: false,
403 line_offset: 0x02,
404 },
405 ]),
406 }],
407 };
408 let json = serde_json::to_string(&make()).unwrap();
409 assert!(json.contains("data_service_id"));
410 assert_eq!(json, serde_json::to_string(&make()).unwrap());
411 }
412}