1use super::descriptor_body;
22use crate::error::{Error, Result};
23use alloc::vec::Vec;
24use dvb_common::{Parse, Serialize};
25
26pub const TAG: u8 = 0x51;
28const HEADER_LEN: usize = 2;
29const GRID_HEADER_LEN: usize = 1;
30const CELL_FIXED_LEN: usize = 3; const MAX_BODY_LEN: usize = u8::MAX as usize;
33const MAX_ELEM_FIELD: usize = u8::MAX as usize;
35
36const ENTRY_POINT_MASK: u8 = 0x80; const ELEM_CELL_ID_MASK: u8 = 0x3F; #[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize))]
42#[non_exhaustive]
43pub enum CellLinkage {
44 Undefined,
46 Bouquet {
48 bouquet_id: u16,
50 },
51 Service {
53 original_network_id: u16,
55 transport_stream_id: u16,
57 service_id: u16,
59 },
60 OtherMosaic {
62 original_network_id: u16,
64 transport_stream_id: u16,
66 service_id: u16,
68 },
69 Event {
71 original_network_id: u16,
73 transport_stream_id: u16,
75 service_id: u16,
77 event_id: u16,
79 },
80 Reserved {
83 value: u8,
85 },
86}
87
88impl CellLinkage {
89 fn info_byte(&self) -> u8 {
91 match self {
92 CellLinkage::Undefined => 0x00,
93 CellLinkage::Bouquet { .. } => 0x01,
94 CellLinkage::Service { .. } => 0x02,
95 CellLinkage::OtherMosaic { .. } => 0x03,
96 CellLinkage::Event { .. } => 0x04,
97 CellLinkage::Reserved { value } => *value,
98 }
99 }
100
101 fn payload_len(&self) -> usize {
103 match self {
104 CellLinkage::Undefined | CellLinkage::Reserved { .. } => 0,
105 CellLinkage::Bouquet { .. } => 2,
106 CellLinkage::Service { .. } | CellLinkage::OtherMosaic { .. } => 6,
107 CellLinkage::Event { .. } => 8,
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize))]
115pub struct MosaicLogicalCell {
116 pub logical_cell_id: u8,
118 pub presentation_info: u8,
121 pub elementary_cell_ids: Vec<u8>,
123 pub linkage: CellLinkage,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize))]
130pub struct MosaicDescriptor {
131 pub mosaic_entry_point: bool,
133 pub num_horizontal_cells: u8,
135 pub num_vertical_cells: u8,
137 pub logical_cells: Vec<MosaicLogicalCell>,
139}
140
141fn read_u16(b: &[u8], at: usize) -> u16 {
142 u16::from_be_bytes([b[at], b[at + 1]])
143}
144
145impl<'a> Parse<'a> for MosaicDescriptor {
146 type Error = crate::error::Error;
147 fn parse(bytes: &'a [u8]) -> Result<Self> {
148 let body = descriptor_body(
149 bytes,
150 TAG,
151 "MosaicDescriptor",
152 "unexpected tag for mosaic_descriptor",
153 )?;
154 if body.len() < GRID_HEADER_LEN {
155 return Err(Error::InvalidDescriptor {
156 tag: TAG,
157 reason: "mosaic_descriptor missing grid header byte",
158 });
159 }
160 let mosaic_entry_point = body[0] & ENTRY_POINT_MASK != 0;
162 let num_horizontal_cells = (body[0] >> 4) & 0x07;
163 let num_vertical_cells = body[0] & 0x07;
164
165 let mut logical_cells = Vec::new();
166 let mut pos = GRID_HEADER_LEN;
167 while pos < body.len() {
168 if pos + CELL_FIXED_LEN > body.len() {
169 return Err(Error::InvalidDescriptor {
170 tag: TAG,
171 reason: "truncated mosaic logical cell header",
172 });
173 }
174 let logical_cell_id = (body[pos] >> 2) & 0x3F;
176 let presentation_info = body[pos + 1] & 0x07;
177 let elem_field_len = body[pos + 2] as usize;
178 pos += CELL_FIXED_LEN;
179 if pos + elem_field_len > body.len() {
180 return Err(Error::InvalidDescriptor {
181 tag: TAG,
182 reason: "elementary_cell_field_length exceeds descriptor body",
183 });
184 }
185 let mut elementary_cell_ids = Vec::with_capacity(elem_field_len);
186 for &b in &body[pos..pos + elem_field_len] {
187 elementary_cell_ids.push(b & ELEM_CELL_ID_MASK);
189 }
190 pos += elem_field_len;
191 if pos >= body.len() {
192 return Err(Error::InvalidDescriptor {
193 tag: TAG,
194 reason: "missing cell_linkage_info byte",
195 });
196 }
197 let info = body[pos];
198 pos += 1;
199 let linkage = match info {
200 0x00 => CellLinkage::Undefined,
201 0x01 => {
202 if pos + 2 > body.len() {
203 return Err(Error::InvalidDescriptor {
204 tag: TAG,
205 reason: "truncated bouquet linkage payload",
206 });
207 }
208 let l = CellLinkage::Bouquet {
209 bouquet_id: read_u16(body, pos),
210 };
211 pos += 2;
212 l
213 }
214 0x02 | 0x03 => {
215 if pos + 6 > body.len() {
216 return Err(Error::InvalidDescriptor {
217 tag: TAG,
218 reason: "truncated service/mosaic linkage payload",
219 });
220 }
221 let original_network_id = read_u16(body, pos);
222 let transport_stream_id = read_u16(body, pos + 2);
223 let service_id = read_u16(body, pos + 4);
224 pos += 6;
225 if info == 0x02 {
226 CellLinkage::Service {
227 original_network_id,
228 transport_stream_id,
229 service_id,
230 }
231 } else {
232 CellLinkage::OtherMosaic {
233 original_network_id,
234 transport_stream_id,
235 service_id,
236 }
237 }
238 }
239 0x04 => {
240 if pos + 8 > body.len() {
241 return Err(Error::InvalidDescriptor {
242 tag: TAG,
243 reason: "truncated event linkage payload",
244 });
245 }
246 let l = CellLinkage::Event {
247 original_network_id: read_u16(body, pos),
248 transport_stream_id: read_u16(body, pos + 2),
249 service_id: read_u16(body, pos + 4),
250 event_id: read_u16(body, pos + 6),
251 };
252 pos += 8;
253 l
254 }
255 other => CellLinkage::Reserved { value: other },
257 };
258 logical_cells.push(MosaicLogicalCell {
259 logical_cell_id,
260 presentation_info,
261 elementary_cell_ids,
262 linkage,
263 });
264 }
265 Ok(Self {
266 mosaic_entry_point,
267 num_horizontal_cells,
268 num_vertical_cells,
269 logical_cells,
270 })
271 }
272}
273
274impl Serialize for MosaicDescriptor {
275 type Error = crate::error::Error;
276 fn serialized_len(&self) -> usize {
277 let cells: usize = self
278 .logical_cells
279 .iter()
280 .map(|c| CELL_FIXED_LEN + c.elementary_cell_ids.len() + 1 + c.linkage.payload_len())
281 .sum();
282 HEADER_LEN + GRID_HEADER_LEN + cells
283 }
284
285 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
286 let len = self.serialized_len();
287 if buf.len() < len {
288 return Err(Error::OutputBufferTooSmall {
289 need: len,
290 have: buf.len(),
291 });
292 }
293 let body_len = len - HEADER_LEN;
294 if body_len > MAX_BODY_LEN {
296 return Err(Error::InvalidDescriptor {
297 tag: TAG,
298 reason: "mosaic_descriptor body exceeds 255 bytes",
299 });
300 }
301 buf[0] = TAG;
302 buf[1] = body_len as u8;
303 buf[2] = if self.mosaic_entry_point {
305 ENTRY_POINT_MASK
306 } else {
307 0
308 } | ((self.num_horizontal_cells & 0x07) << 4)
309 | 0x08
310 | (self.num_vertical_cells & 0x07);
311 let mut pos = HEADER_LEN + GRID_HEADER_LEN;
312 for cell in &self.logical_cells {
313 if cell.elementary_cell_ids.len() > MAX_ELEM_FIELD {
315 return Err(Error::InvalidDescriptor {
316 tag: TAG,
317 reason: "elementary_cell_field exceeds 255 entries (8-bit length field)",
318 });
319 }
320 buf[pos] = ((cell.logical_cell_id & 0x3F) << 2) | 0x03;
322 buf[pos + 1] = 0xF8 | (cell.presentation_info & 0x07);
324 buf[pos + 2] = cell.elementary_cell_ids.len() as u8;
325 pos += CELL_FIXED_LEN;
326 for &id in &cell.elementary_cell_ids {
327 buf[pos] = 0xC0 | (id & ELEM_CELL_ID_MASK);
329 pos += 1;
330 }
331 buf[pos] = cell.linkage.info_byte();
332 pos += 1;
333 match &cell.linkage {
334 CellLinkage::Undefined | CellLinkage::Reserved { .. } => {}
335 CellLinkage::Bouquet { bouquet_id } => {
336 buf[pos..pos + 2].copy_from_slice(&bouquet_id.to_be_bytes());
337 pos += 2;
338 }
339 CellLinkage::Service {
340 original_network_id,
341 transport_stream_id,
342 service_id,
343 }
344 | CellLinkage::OtherMosaic {
345 original_network_id,
346 transport_stream_id,
347 service_id,
348 } => {
349 buf[pos..pos + 2].copy_from_slice(&original_network_id.to_be_bytes());
350 buf[pos + 2..pos + 4].copy_from_slice(&transport_stream_id.to_be_bytes());
351 buf[pos + 4..pos + 6].copy_from_slice(&service_id.to_be_bytes());
352 pos += 6;
353 }
354 CellLinkage::Event {
355 original_network_id,
356 transport_stream_id,
357 service_id,
358 event_id,
359 } => {
360 buf[pos..pos + 2].copy_from_slice(&original_network_id.to_be_bytes());
361 buf[pos + 2..pos + 4].copy_from_slice(&transport_stream_id.to_be_bytes());
362 buf[pos + 4..pos + 6].copy_from_slice(&service_id.to_be_bytes());
363 buf[pos + 6..pos + 8].copy_from_slice(&event_id.to_be_bytes());
364 pos += 8;
365 }
366 }
367 }
368 Ok(len)
369 }
370}
371impl<'a> crate::traits::DescriptorDef<'a> for MosaicDescriptor {
372 const TAG: u8 = TAG;
373 const NAME: &'static str = "MOSAIC";
374}
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379
380 #[test]
381 fn parse_single_cell_undefined_linkage() {
382 let bytes = [
385 TAG,
386 0x07,
387 0x80 | (1 << 4) | 0x08 | 1, (5 << 2) | 0x03, 0xF8 | 1, 0x02, 0xC0 | 3,
392 0xC0 | 7,
393 0x00, ];
395 let d = MosaicDescriptor::parse(&bytes).unwrap();
396 assert!(d.mosaic_entry_point);
397 assert_eq!(d.num_horizontal_cells, 1);
398 assert_eq!(d.num_vertical_cells, 1);
399 assert_eq!(d.logical_cells.len(), 1);
400 let c = &d.logical_cells[0];
401 assert_eq!(c.logical_cell_id, 5);
402 assert_eq!(c.presentation_info, 1);
403 assert_eq!(c.elementary_cell_ids, vec![3, 7]);
404 assert_eq!(c.linkage, CellLinkage::Undefined);
405 }
406
407 #[test]
408 fn parse_service_linkage() {
409 let d = MosaicDescriptor {
410 mosaic_entry_point: false,
411 num_horizontal_cells: 0,
412 num_vertical_cells: 0,
413 logical_cells: vec![MosaicLogicalCell {
414 logical_cell_id: 1,
415 presentation_info: 2,
416 elementary_cell_ids: vec![0],
417 linkage: CellLinkage::Service {
418 original_network_id: 0x1111,
419 transport_stream_id: 0x2222,
420 service_id: 0x3333,
421 },
422 }],
423 };
424 let mut buf = vec![0u8; d.serialized_len()];
425 d.serialize_into(&mut buf).unwrap();
426 let p = MosaicDescriptor::parse(&buf).unwrap();
427 assert_eq!(p, d);
428 assert!(matches!(
429 p.logical_cells[0].linkage,
430 CellLinkage::Service {
431 original_network_id: 0x1111,
432 transport_stream_id: 0x2222,
433 service_id: 0x3333,
434 }
435 ));
436 }
437
438 #[test]
439 fn parse_event_linkage_round_trip() {
440 let d = MosaicDescriptor {
441 mosaic_entry_point: true,
442 num_horizontal_cells: 3,
443 num_vertical_cells: 2,
444 logical_cells: vec![MosaicLogicalCell {
445 logical_cell_id: 10,
446 presentation_info: 3,
447 elementary_cell_ids: vec![1, 2, 3],
448 linkage: CellLinkage::Event {
449 original_network_id: 0xAAAA,
450 transport_stream_id: 0xBBBB,
451 service_id: 0xCCCC,
452 event_id: 0xDDDD,
453 },
454 }],
455 };
456 let mut buf = vec![0u8; d.serialized_len()];
457 d.serialize_into(&mut buf).unwrap();
458 assert_eq!(MosaicDescriptor::parse(&buf).unwrap(), d);
459 }
460
461 #[test]
462 fn parse_reserved_linkage_round_trips() {
463 let bytes = [
464 TAG,
465 0x05,
466 0x08, (1 << 2) | 0x03,
468 0xF8,
469 0x00, 0x42, ];
472 let d = MosaicDescriptor::parse(&bytes).unwrap();
473 assert_eq!(
474 d.logical_cells[0].linkage,
475 CellLinkage::Reserved { value: 0x42 }
476 );
477 let mut buf = vec![0u8; d.serialized_len()];
478 d.serialize_into(&mut buf).unwrap();
479 assert_eq!(MosaicDescriptor::parse(&buf).unwrap(), d);
480 }
481
482 #[test]
483 fn parse_multiple_cells() {
484 let d = MosaicDescriptor {
485 mosaic_entry_point: false,
486 num_horizontal_cells: 1,
487 num_vertical_cells: 1,
488 logical_cells: vec![
489 MosaicLogicalCell {
490 logical_cell_id: 0,
491 presentation_info: 1,
492 elementary_cell_ids: vec![0],
493 linkage: CellLinkage::Bouquet { bouquet_id: 0x1234 },
494 },
495 MosaicLogicalCell {
496 logical_cell_id: 1,
497 presentation_info: 1,
498 elementary_cell_ids: vec![1],
499 linkage: CellLinkage::Undefined,
500 },
501 ],
502 };
503 let mut buf = vec![0u8; d.serialized_len()];
504 d.serialize_into(&mut buf).unwrap();
505 let p = MosaicDescriptor::parse(&buf).unwrap();
506 assert_eq!(p.logical_cells.len(), 2);
507 assert_eq!(p, d);
508 }
509
510 #[test]
511 fn parse_rejects_wrong_tag() {
512 assert!(matches!(
513 MosaicDescriptor::parse(&[0x52, 1, 0x08]).unwrap_err(),
514 Error::InvalidDescriptor { tag: 0x52, .. }
515 ));
516 }
517
518 #[test]
519 fn parse_rejects_short_buffer() {
520 let bytes = [TAG, 5, 0x08, 0x00];
522 assert!(matches!(
523 MosaicDescriptor::parse(&bytes).unwrap_err(),
524 Error::BufferTooShort { .. }
525 ));
526 }
527
528 #[test]
529 fn parse_rejects_elem_field_overrun() {
530 let bytes = [TAG, 4, 0x08, (1 << 2) | 0x03, 0xF8, 0x0A];
532 assert!(matches!(
533 MosaicDescriptor::parse(&bytes).unwrap_err(),
534 Error::InvalidDescriptor { tag: TAG, .. }
535 ));
536 }
537
538 #[test]
539 fn parse_rejects_missing_linkage_byte() {
540 let bytes = [TAG, 4, 0x08, (1 << 2) | 0x03, 0xF8, 0x00];
542 assert!(matches!(
545 MosaicDescriptor::parse(&bytes).unwrap_err(),
546 Error::InvalidDescriptor { tag: TAG, .. }
547 ));
548 }
549
550 #[test]
551 fn parse_rejects_zero_length() {
552 assert!(matches!(
553 MosaicDescriptor::parse(&[TAG, 0]).unwrap_err(),
554 Error::InvalidDescriptor { tag: TAG, .. }
555 ));
556 }
557
558 #[test]
559 fn serialize_emits_reserved_ones() {
560 let d = MosaicDescriptor {
561 mosaic_entry_point: false,
562 num_horizontal_cells: 0,
563 num_vertical_cells: 0,
564 logical_cells: vec![],
565 };
566 let mut buf = vec![0u8; d.serialized_len()];
567 d.serialize_into(&mut buf).unwrap();
568 assert_eq!(buf[2] & 0x08, 0x08);
570 }
571
572 #[test]
573 fn serialize_round_trip_empty_grid() {
574 let d = MosaicDescriptor {
575 mosaic_entry_point: true,
576 num_horizontal_cells: 7,
577 num_vertical_cells: 7,
578 logical_cells: vec![],
579 };
580 let mut buf = vec![0u8; d.serialized_len()];
581 d.serialize_into(&mut buf).unwrap();
582 assert_eq!(MosaicDescriptor::parse(&buf).unwrap(), d);
583 }
584
585 #[cfg(feature = "serde")]
586 #[test]
587 fn serde_round_trip() {
588 let d = MosaicDescriptor {
589 mosaic_entry_point: true,
590 num_horizontal_cells: 1,
591 num_vertical_cells: 1,
592 logical_cells: vec![MosaicLogicalCell {
593 logical_cell_id: 5,
594 presentation_info: 1,
595 elementary_cell_ids: vec![3, 7],
596 linkage: CellLinkage::Bouquet { bouquet_id: 0x1234 },
597 }],
598 };
599 let json = serde_json::to_string(&d).unwrap();
600 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
602 }
603}