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