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