1use super::descriptor_body;
17use crate::error::{Error, Result};
18use dvb_common::{Parse, Serialize};
19
20pub const TAG: u8 = 0x4A;
22const HEADER_LEN: usize = 2;
23const FIXED_FIELDS_LEN: usize = 7;
24
25const HANDOVER_TYPE_MASK: u8 = 0xF0;
26const ORIGIN_TYPE_MASK: u8 = 0x01;
27const RESERVED_HANDOVER_MASK: u8 = 0x0E;
28
29const TARGET_LISTED_MASK: u8 = 0x80;
30const EVENT_SIMULCAST_MASK: u8 = 0x40;
31const RESERVED_EVENT_MASK: u8 = 0x3F;
32
33const EXT_TARGET_LISTED_MASK: u8 = 0x80;
34const EXT_EVENT_SIMULCAST_MASK: u8 = 0x40;
35const EXT_LINK_TYPE_MASK: u8 = 0x30;
36const EXT_TARGET_ID_TYPE_MASK: u8 = 0x0C;
37const EXT_ONID_FLAG_MASK: u8 = 0x02;
38const EXT_SID_FLAG_MASK: u8 = 0x01;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize))]
43#[non_exhaustive]
44pub enum LinkageType {
45 InformationService,
47 EpgService,
49 CaReplacementService,
51 TsContainingCompleteSi,
53 ServiceReplacementService,
55 DataBroadcastService,
57 RcsMap,
59 MobileHandOver,
61 SsuService,
63 TsContainingSsuBatOrNit,
65 IpMacNotificationService,
67 TsContainingIntBatOrNit,
69 EventLinkage,
71 ExtendedEventLinkage(u8),
73 DownloadableFontInfoLinkage,
75 NativeIpBootstrapMpeStream,
77 Reserved(u8),
79}
80
81impl LinkageType {
82 #[must_use]
83 pub fn from_u8(v: u8) -> Self {
86 match v {
87 0x01 => Self::InformationService,
88 0x02 => Self::EpgService,
89 0x03 => Self::CaReplacementService,
90 0x04 => Self::TsContainingCompleteSi,
91 0x05 => Self::ServiceReplacementService,
92 0x06 => Self::DataBroadcastService,
93 0x07 => Self::RcsMap,
94 0x08 => Self::MobileHandOver,
95 0x09 => Self::SsuService,
96 0x0A => Self::TsContainingSsuBatOrNit,
97 0x0B => Self::IpMacNotificationService,
98 0x0C => Self::TsContainingIntBatOrNit,
99 0x0D => Self::EventLinkage,
100 0x0E..=0x1F => Self::ExtendedEventLinkage(v),
101 0x20 => Self::DownloadableFontInfoLinkage,
102 0x21 => Self::NativeIpBootstrapMpeStream,
103 v => Self::Reserved(v),
104 }
105 }
106
107 #[must_use]
108 pub fn to_u8(self) -> u8 {
110 match self {
111 Self::InformationService => 0x01,
112 Self::EpgService => 0x02,
113 Self::CaReplacementService => 0x03,
114 Self::TsContainingCompleteSi => 0x04,
115 Self::ServiceReplacementService => 0x05,
116 Self::DataBroadcastService => 0x06,
117 Self::RcsMap => 0x07,
118 Self::MobileHandOver => 0x08,
119 Self::SsuService => 0x09,
120 Self::TsContainingSsuBatOrNit => 0x0A,
121 Self::IpMacNotificationService => 0x0B,
122 Self::TsContainingIntBatOrNit => 0x0C,
123 Self::EventLinkage => 0x0D,
124 Self::ExtendedEventLinkage(v) => v,
125 Self::DownloadableFontInfoLinkage => 0x20,
126 Self::NativeIpBootstrapMpeStream => 0x21,
127 Self::Reserved(v) => v,
128 }
129 }
130
131 #[must_use]
132 pub fn name(self) -> &'static str {
134 match self {
135 Self::InformationService => "information service",
136 Self::EpgService => "EPG service",
137 Self::CaReplacementService => "CA replacement service",
138 Self::TsContainingCompleteSi => "TS containing complete network/bouquet SI",
139 Self::ServiceReplacementService => "service replacement service",
140 Self::DataBroadcastService => "data broadcast service",
141 Self::RcsMap => "RCS map",
142 Self::MobileHandOver => "mobile hand-over",
143 Self::SsuService => "SSU service",
144 Self::TsContainingSsuBatOrNit => "TS containing SSU BAT or NIT",
145 Self::IpMacNotificationService => "IP/MAC notification service",
146 Self::TsContainingIntBatOrNit => "TS containing INT BAT or NIT",
147 Self::EventLinkage => "event linkage",
148 Self::ExtendedEventLinkage(_) => "extended event linkage",
149 Self::DownloadableFontInfoLinkage => "downloadable font info linkage",
150 Self::NativeIpBootstrapMpeStream => "Native IP bootstrap MPE stream",
151 Self::Reserved(_) => "reserved",
152 }
153 }
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize))]
159#[non_exhaustive]
160pub enum HandOverType {
161 DvbIdenticalNeighbouringCountry,
163 DvbLocalVariation,
165 DvbAssociatedService,
167 Reserved(u8),
169}
170
171impl HandOverType {
172 #[must_use]
173 pub fn from_u8(v: u8) -> Self {
176 match v {
177 0x1 => Self::DvbIdenticalNeighbouringCountry,
178 0x2 => Self::DvbLocalVariation,
179 0x3 => Self::DvbAssociatedService,
180 v => Self::Reserved(v),
181 }
182 }
183
184 #[must_use]
185 pub fn to_u8(self) -> u8 {
187 match self {
188 Self::DvbIdenticalNeighbouringCountry => 0x1,
189 Self::DvbLocalVariation => 0x2,
190 Self::DvbAssociatedService => 0x3,
191 Self::Reserved(v) => v,
192 }
193 }
194
195 #[must_use]
196 pub fn name(self) -> &'static str {
198 match self {
199 Self::DvbIdenticalNeighbouringCountry => {
200 "DVB hand-over to an identical service in a neighbouring country"
201 }
202 Self::DvbLocalVariation => "DVB hand-over to a local variation of the same service",
203 Self::DvbAssociatedService => "DVB hand-over to an associated service",
204 Self::Reserved(_) => "reserved",
205 }
206 }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211#[cfg_attr(feature = "serde", derive(serde::Serialize))]
212#[non_exhaustive]
213pub enum LinkType {
214 SdOrUhd,
216 HdOrServiceFrameCompatible,
219 FrameCompatiblePlanoStereoscopic,
221 ServiceCompatiblePlanoStereoscopicMvc,
223 Reserved(u8),
225}
226
227impl LinkType {
228 #[must_use]
229 pub fn from_u8(v: u8) -> Self {
232 match v {
233 0 => Self::SdOrUhd,
234 1 => Self::HdOrServiceFrameCompatible,
235 2 => Self::FrameCompatiblePlanoStereoscopic,
236 3 => Self::ServiceCompatiblePlanoStereoscopicMvc,
237 v => Self::Reserved(v),
238 }
239 }
240
241 #[must_use]
242 pub fn to_u8(self) -> u8 {
244 match self {
245 Self::SdOrUhd => 0,
246 Self::HdOrServiceFrameCompatible => 1,
247 Self::FrameCompatiblePlanoStereoscopic => 2,
248 Self::ServiceCompatiblePlanoStereoscopicMvc => 3,
249 Self::Reserved(v) => v,
250 }
251 }
252
253 #[must_use]
254 pub fn name(self) -> &'static str {
256 match self {
257 Self::SdOrUhd => "SD (0x0E) / UHD (0x0F)",
258 Self::HdOrServiceFrameCompatible => {
259 "HD (0x0E) / service frame compatible plano-stereoscopic (0x0F)"
260 }
261 Self::FrameCompatiblePlanoStereoscopic => {
262 "frame compatible plano-stereoscopic H.264/AVC"
263 }
264 Self::ServiceCompatiblePlanoStereoscopicMvc => {
265 "service compatible plano-stereoscopic MVC"
266 }
267 Self::Reserved(_) => "reserved",
268 }
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274#[cfg_attr(feature = "serde", derive(serde::Serialize))]
275#[non_exhaustive]
276pub enum TargetIdType {
277 UseTransportStreamId,
279 UseTargetTransportStreamId,
281 MatchAnyTransportStreamId,
283 UseUserDefinedId,
285 Reserved(u8),
287}
288
289impl TargetIdType {
290 #[must_use]
291 pub fn from_u8(v: u8) -> Self {
294 match v {
295 0 => Self::UseTransportStreamId,
296 1 => Self::UseTargetTransportStreamId,
297 2 => Self::MatchAnyTransportStreamId,
298 3 => Self::UseUserDefinedId,
299 v => Self::Reserved(v),
300 }
301 }
302
303 #[must_use]
304 pub fn to_u8(self) -> u8 {
306 match self {
307 Self::UseTransportStreamId => 0,
308 Self::UseTargetTransportStreamId => 1,
309 Self::MatchAnyTransportStreamId => 2,
310 Self::UseUserDefinedId => 3,
311 Self::Reserved(v) => v,
312 }
313 }
314
315 #[must_use]
316 pub fn name(self) -> &'static str {
318 match self {
319 Self::UseTransportStreamId => "use transport_stream_id",
320 Self::UseTargetTransportStreamId => "use target_transport_stream_id",
321 Self::MatchAnyTransportStreamId => "match any transport_stream_id (wildcard)",
322 Self::UseUserDefinedId => "use user_defined_id",
323 Self::Reserved(_) => "reserved",
324 }
325 }
326}
327
328#[derive(Debug, Clone, PartialEq, Eq)]
330#[cfg_attr(feature = "serde", derive(serde::Serialize))]
331pub struct MobileHandOverInfo {
332 pub hand_over_type: HandOverType,
334 pub origin_type: bool,
336 pub network_id: Option<u16>,
338 pub initial_service_id: Option<u16>,
340}
341
342impl MobileHandOverInfo {
343 fn serialized_len(&self) -> usize {
344 1 + self.network_id.map_or(0, |_| 2) + self.initial_service_id.map_or(0, |_| 2)
345 }
346
347 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
348 let len = self.serialized_len();
349 if buf.len() < len {
350 return Err(Error::OutputBufferTooSmall {
351 need: len,
352 have: buf.len(),
353 });
354 }
355 let flags_byte = (self.hand_over_type.to_u8() << 4)
356 | RESERVED_HANDOVER_MASK
357 | u8::from(self.origin_type);
358 buf[0] = flags_byte;
359 let mut pos = 1;
360 if let Some(nid) = self.network_id {
361 buf[pos..pos + 2].copy_from_slice(&nid.to_be_bytes());
362 pos += 2;
363 }
364 if let Some(sid) = self.initial_service_id {
365 buf[pos..pos + 2].copy_from_slice(&sid.to_be_bytes());
366 }
367 Ok(len)
368 }
369}
370
371#[derive(Debug, Clone, PartialEq, Eq)]
373#[cfg_attr(feature = "serde", derive(serde::Serialize))]
374pub struct EventLinkageInfo {
375 pub target_event_id: u16,
377 pub target_listed: bool,
379 pub event_simulcast: bool,
381}
382
383impl EventLinkageInfo {
384 const SERIALIZED_LEN: usize = 3;
385
386 fn serialized_len(&self) -> usize {
387 Self::SERIALIZED_LEN
388 }
389
390 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
391 if buf.len() < Self::SERIALIZED_LEN {
392 return Err(Error::OutputBufferTooSmall {
393 need: Self::SERIALIZED_LEN,
394 have: buf.len(),
395 });
396 }
397 buf[0..2].copy_from_slice(&self.target_event_id.to_be_bytes());
398 let mut byte2: u8 = RESERVED_EVENT_MASK;
399 if self.target_listed {
400 byte2 |= TARGET_LISTED_MASK;
401 }
402 if self.event_simulcast {
403 byte2 |= EVENT_SIMULCAST_MASK;
404 }
405 buf[2] = byte2;
406 Ok(Self::SERIALIZED_LEN)
407 }
408}
409
410#[derive(Debug, Clone, PartialEq, Eq)]
412#[cfg_attr(feature = "serde", derive(serde::Serialize))]
413pub enum TargetId {
414 UserDefined {
416 user_defined_id: u16,
418 },
419 Dvb {
421 target_id_type: TargetIdType,
423 target_transport_stream_id: Option<u16>,
425 target_original_network_id: Option<u16>,
427 target_service_id: Option<u16>,
429 },
430}
431
432impl TargetId {
433 fn serialized_len(&self) -> usize {
434 match self {
435 TargetId::UserDefined { .. } => 2,
436 TargetId::Dvb {
437 target_transport_stream_id,
438 target_original_network_id,
439 target_service_id,
440 ..
441 } => {
442 usize::from(target_transport_stream_id.is_some()) * 2
443 + usize::from(target_original_network_id.is_some()) * 2
444 + usize::from(target_service_id.is_some()) * 2
445 }
446 }
447 }
448
449 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
450 let len = self.serialized_len();
451 if buf.len() < len {
452 return Err(Error::OutputBufferTooSmall {
453 need: len,
454 have: buf.len(),
455 });
456 }
457 match self {
458 TargetId::UserDefined { user_defined_id } => {
459 buf[..2].copy_from_slice(&user_defined_id.to_be_bytes());
460 }
461 TargetId::Dvb {
462 target_transport_stream_id,
463 target_original_network_id,
464 target_service_id,
465 ..
466 } => {
467 let ts_len = target_transport_stream_id.map_or(0, |_| 2);
468 let onid_len = target_original_network_id.map_or(0, |_| 2);
469 if let Some(ts_id) = target_transport_stream_id {
470 buf[..2].copy_from_slice(&ts_id.to_be_bytes());
471 }
472 if let Some(onid) = target_original_network_id {
473 buf[ts_len..ts_len + 2].copy_from_slice(&onid.to_be_bytes());
474 }
475 if let Some(sid) = target_service_id {
476 let off = ts_len + onid_len;
477 buf[off..off + 2].copy_from_slice(&sid.to_be_bytes());
478 }
479 }
480 }
481 Ok(len)
482 }
483}
484
485#[derive(Debug, Clone, PartialEq, Eq)]
487#[cfg_attr(feature = "serde", derive(serde::Serialize))]
488pub struct ExtendedEventLinkageEntry {
489 pub target_event_id: u16,
491 pub target_listed: bool,
493 pub event_simulcast: bool,
495 pub link_type: LinkType,
497 pub target_id_type: TargetIdType,
499 pub original_network_id_flag: bool,
501 pub service_id_flag: bool,
503 pub target_id: TargetId,
505}
506
507impl ExtendedEventLinkageEntry {
508 fn serialized_len(&self) -> usize {
509 2 + 1 + self.target_id.serialized_len()
510 }
511
512 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
513 let flags_byte_len = 3;
514 if buf.len() < flags_byte_len {
515 return Err(Error::OutputBufferTooSmall {
516 need: flags_byte_len,
517 have: buf.len(),
518 });
519 }
520 buf[0..2].copy_from_slice(&self.target_event_id.to_be_bytes());
521 let mut byte2: u8 = 0;
522 if self.target_listed {
523 byte2 |= EXT_TARGET_LISTED_MASK;
524 }
525 if self.event_simulcast {
526 byte2 |= EXT_EVENT_SIMULCAST_MASK;
527 }
528 byte2 |= (self.link_type.to_u8() & 0x03) << 4;
529 byte2 |= (self.target_id_type.to_u8() & 0x03) << 2;
530 if self.original_network_id_flag {
531 byte2 |= EXT_ONID_FLAG_MASK;
532 }
533 if self.service_id_flag {
534 byte2 |= EXT_SID_FLAG_MASK;
535 }
536 buf[2] = byte2;
537 let tid_written = self.target_id.serialize_into(&mut buf[3..])?;
538 Ok(3 + tid_written)
539 }
540}
541
542#[derive(Debug, Clone, PartialEq, Eq)]
545#[cfg_attr(feature = "serde", derive(serde::Serialize))]
546pub struct ExtendedEventLinkageInfo {
547 pub entries: Vec<ExtendedEventLinkageEntry>,
549}
550
551impl ExtendedEventLinkageInfo {
552 fn serialized_len(&self) -> usize {
553 1 + self
554 .entries
555 .iter()
556 .map(|e| e.serialized_len())
557 .sum::<usize>()
558 }
559
560 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
561 let loop_len: usize = self.entries.iter().map(|e| e.serialized_len()).sum();
562 let total = 1 + loop_len;
563 if buf.len() < total {
564 return Err(Error::OutputBufferTooSmall {
565 need: total,
566 have: buf.len(),
567 });
568 }
569 buf[0] = loop_len as u8;
570 let mut pos = 1;
571 for entry in &self.entries {
572 let written = entry.serialize_into(&mut buf[pos..])?;
573 pos += written;
574 }
575 Ok(total)
576 }
577}
578
579#[derive(Debug, Clone, PartialEq, Eq)]
589#[non_exhaustive]
590#[cfg_attr(feature = "serde", derive(serde::Serialize))]
591pub enum LinkageData<'a> {
592 MobileHandOver(MobileHandOverInfo),
594 EventLinkage(EventLinkageInfo),
596 ExtendedEventLinkage(ExtendedEventLinkageInfo),
598 None,
601 #[cfg_attr(feature = "serde", serde(borrow))]
606 Other(&'a [u8]),
607}
608
609impl LinkageData<'_> {
610 fn serialized_len(&self) -> usize {
611 match self {
612 LinkageData::MobileHandOver(m) => m.serialized_len(),
613 LinkageData::EventLinkage(e) => e.serialized_len(),
614 LinkageData::ExtendedEventLinkage(x) => x.serialized_len(),
615 LinkageData::None => 0,
616 LinkageData::Other(b) => b.len(),
617 }
618 }
619
620 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
621 match self {
622 LinkageData::MobileHandOver(m) => m.serialize_into(buf),
623 LinkageData::EventLinkage(e) => e.serialize_into(buf),
624 LinkageData::ExtendedEventLinkage(x) => x.serialize_into(buf),
625 LinkageData::None => Ok(0),
626 LinkageData::Other(b) => {
627 if buf.len() < b.len() {
628 return Err(Error::OutputBufferTooSmall {
629 need: b.len(),
630 have: buf.len(),
631 });
632 }
633 buf[..b.len()].copy_from_slice(b);
634 Ok(b.len())
635 }
636 }
637 }
638}
639
640fn parse_mobile_handover(bytes: &[u8], end: usize) -> Result<MobileHandOverInfo> {
641 if end < 1 {
642 return Err(Error::InvalidDescriptor {
643 tag: TAG,
644 reason: "mobile hand-over info needs at least flags byte",
645 });
646 }
647 let flags_byte = bytes[0];
648 let hand_over_type = HandOverType::from_u8((flags_byte & HANDOVER_TYPE_MASK) >> 4);
649 let origin_type = (flags_byte & ORIGIN_TYPE_MASK) != 0;
650 let mut pos = 1;
651 let network_id = if matches!(hand_over_type.to_u8(), 0x01..=0x03) {
652 if pos + 2 > end {
653 return Err(Error::InvalidDescriptor {
654 tag: TAG,
655 reason: "mobile hand-over info with gated network_id needs at least 3 bytes",
656 });
657 }
658 let nid = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
659 pos += 2;
660 Some(nid)
661 } else {
662 None
663 };
664 let initial_service_id = if !origin_type {
665 if pos + 2 > end {
666 return Err(Error::InvalidDescriptor {
667 tag: TAG,
668 reason: "mobile hand-over info with origin_type=NIT needs initial_service_id",
669 });
670 }
671 Some(u16::from_be_bytes([bytes[pos], bytes[pos + 1]]))
672 } else {
673 None
674 };
675 Ok(MobileHandOverInfo {
676 hand_over_type,
677 origin_type,
678 network_id,
679 initial_service_id,
680 })
681}
682
683fn parse_event_linkage(bytes: &[u8]) -> Result<EventLinkageInfo> {
684 if bytes.len() < 3 {
685 return Err(Error::InvalidDescriptor {
686 tag: TAG,
687 reason: "event linkage info needs 3 bytes",
688 });
689 }
690 let target_event_id = u16::from_be_bytes([bytes[0], bytes[1]]);
691 let target_listed = (bytes[2] & TARGET_LISTED_MASK) != 0;
692 let event_simulcast = (bytes[2] & EVENT_SIMULCAST_MASK) != 0;
693 Ok(EventLinkageInfo {
694 target_event_id,
695 target_listed,
696 event_simulcast,
697 })
698}
699
700fn parse_extended_event_linkage(bytes: &[u8]) -> Result<ExtendedEventLinkageInfo> {
701 if bytes.is_empty() {
702 return Err(Error::InvalidDescriptor {
703 tag: TAG,
704 reason: "extended event linkage info needs at least loop_length byte",
705 });
706 }
707 let loop_length = bytes[0] as usize;
708 let loop_end = 1 + loop_length;
709 if bytes.len() < loop_end {
710 return Err(Error::BufferTooShort {
711 need: loop_end,
712 have: bytes.len(),
713 what: "extended event linkage info loop",
714 });
715 }
716 let mut entries = Vec::new();
717 let mut pos = 1;
718 let read_u16 = |p: &mut usize| -> Result<u16> {
719 if *p + 2 > loop_end {
720 return Err(Error::InvalidDescriptor {
721 tag: TAG,
722 reason: "extended event linkage entry truncated (need u16)",
723 });
724 }
725 let v = u16::from_be_bytes([bytes[*p], bytes[*p + 1]]);
726 *p += 2;
727 Ok(v)
728 };
729 while pos < loop_end {
730 let target_event_id = read_u16(&mut pos)?;
731 if pos >= loop_end {
732 return Err(Error::InvalidDescriptor {
733 tag: TAG,
734 reason: "extended event linkage entry truncated (need flags byte)",
735 });
736 }
737 let fb = bytes[pos];
738 pos += 1;
739 let target_listed = (fb & EXT_TARGET_LISTED_MASK) != 0;
740 let event_simulcast = (fb & EXT_EVENT_SIMULCAST_MASK) != 0;
741 let link_type = LinkType::from_u8((fb & EXT_LINK_TYPE_MASK) >> 4);
742 let target_id_type = TargetIdType::from_u8((fb & EXT_TARGET_ID_TYPE_MASK) >> 2);
743 let original_network_id_flag = (fb & EXT_ONID_FLAG_MASK) != 0;
744 let service_id_flag = (fb & EXT_SID_FLAG_MASK) != 0;
745
746 let target_id = if target_id_type == TargetIdType::UseUserDefinedId {
747 let user_defined_id = read_u16(&mut pos)?;
748 TargetId::UserDefined { user_defined_id }
749 } else {
750 let target_transport_stream_id =
751 if target_id_type == TargetIdType::UseTargetTransportStreamId {
752 Some(read_u16(&mut pos)?)
753 } else {
754 None
755 };
756 let target_original_network_id = if original_network_id_flag {
757 Some(read_u16(&mut pos)?)
758 } else {
759 None
760 };
761 let target_service_id = if service_id_flag {
762 Some(read_u16(&mut pos)?)
763 } else {
764 None
765 };
766 TargetId::Dvb {
767 target_id_type,
768 target_transport_stream_id,
769 target_original_network_id,
770 target_service_id,
771 }
772 };
773 entries.push(ExtendedEventLinkageEntry {
774 target_event_id,
775 target_listed,
776 event_simulcast,
777 link_type,
778 target_id_type,
779 original_network_id_flag,
780 service_id_flag,
781 target_id,
782 });
783 }
784 Ok(ExtendedEventLinkageInfo { entries })
785}
786
787#[derive(Debug, Clone, PartialEq, Eq)]
789#[cfg_attr(feature = "serde", derive(serde::Serialize))]
790#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
791pub struct LinkageDescriptor<'a> {
792 pub transport_stream_id: u16,
794 pub original_network_id: u16,
796 pub service_id: u16,
799 pub linkage_type: LinkageType,
801 pub linkage_data: LinkageData<'a>,
803 pub private_data: &'a [u8],
806}
807
808const LINKAGE_TYPES_WITH_OTHER: &[u8] = &[0x09, 0x0A, 0x0B, 0x0C, 0x20, 0x21];
809
810impl<'a> Parse<'a> for LinkageDescriptor<'a> {
811 type Error = crate::error::Error;
812 fn parse(bytes: &'a [u8]) -> Result<Self> {
813 let body = descriptor_body(
814 bytes,
815 TAG,
816 "LinkageDescriptor",
817 "unexpected tag for linkage_descriptor",
818 )?;
819 if body.len() < FIXED_FIELDS_LEN {
820 return Err(Error::InvalidDescriptor {
821 tag: TAG,
822 reason: "linkage_descriptor body shorter than minimum 7 bytes",
823 });
824 }
825 let transport_stream_id = u16::from_be_bytes([body[0], body[1]]);
826 let original_network_id = u16::from_be_bytes([body[2], body[3]]);
827 let service_id = u16::from_be_bytes([body[4], body[5]]);
828 let linkage_type_raw = body[6];
829 let linkage_type = LinkageType::from_u8(linkage_type_raw);
830 let tail = &body[FIXED_FIELDS_LEN..];
831 let tail_len = tail.len();
832
833 let (linkage_data, private_data) = match linkage_type_raw {
834 0x08 => {
835 let info = parse_mobile_handover(tail, tail_len)?;
836 let consumed = info.serialized_len();
837 (LinkageData::MobileHandOver(info), &tail[consumed..])
838 }
839 0x0D => {
840 let info = parse_event_linkage(tail)?;
841 let consumed = EventLinkageInfo::SERIALIZED_LEN;
842 (LinkageData::EventLinkage(info), &tail[consumed..])
843 }
844 0x0E..=0x1F => {
845 let info = parse_extended_event_linkage(tail)?;
846 let consumed = info.serialized_len();
847 (LinkageData::ExtendedEventLinkage(info), &tail[consumed..])
848 }
849 lt if LINKAGE_TYPES_WITH_OTHER.contains(<) || (0x80..=0xFE).contains(<) => {
850 (LinkageData::Other(tail), &[] as &[u8])
851 }
852 _ => (LinkageData::None, tail),
853 };
854 Ok(Self {
855 transport_stream_id,
856 original_network_id,
857 service_id,
858 linkage_type,
859 linkage_data,
860 private_data,
861 })
862 }
863}
864
865impl Serialize for LinkageDescriptor<'_> {
866 type Error = crate::error::Error;
867 fn serialized_len(&self) -> usize {
868 HEADER_LEN + FIXED_FIELDS_LEN + self.linkage_data.serialized_len() + self.private_data.len()
869 }
870
871 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
872 let len = self.serialized_len();
873 if buf.len() < len {
874 return Err(Error::OutputBufferTooSmall {
875 need: len,
876 have: buf.len(),
877 });
878 }
879 buf[0] = TAG;
880 buf[1] = (len - HEADER_LEN) as u8;
881 let bs = HEADER_LEN;
882 buf[bs..bs + 2].copy_from_slice(&self.transport_stream_id.to_be_bytes());
883 buf[bs + 2..bs + 4].copy_from_slice(&self.original_network_id.to_be_bytes());
884 buf[bs + 4..bs + 6].copy_from_slice(&self.service_id.to_be_bytes());
885 buf[bs + 6] = self.linkage_type.to_u8();
886 let ld_start = bs + FIXED_FIELDS_LEN;
887 let ld_written = self.linkage_data.serialize_into(&mut buf[ld_start..])?;
888 let pd_start = ld_start + ld_written;
889 if !self.private_data.is_empty() {
890 buf[pd_start..pd_start + self.private_data.len()].copy_from_slice(self.private_data);
891 }
892 Ok(len)
893 }
894}
895impl<'a> crate::traits::DescriptorDef<'a> for LinkageDescriptor<'a> {
896 const TAG: u8 = TAG;
897 const NAME: &'static str = "LINKAGE";
898}
899
900#[cfg(test)]
901mod tests {
902 use super::*;
903
904 #[test]
905 fn parse_extracts_tsid_onid_sid() {
906 let bytes = [
907 TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB,
908 ];
909 let d = LinkageDescriptor::parse(&bytes).unwrap();
910 assert_eq!(d.transport_stream_id, 0x0001);
911 assert_eq!(d.original_network_id, 0x0002);
912 assert_eq!(d.service_id, 0x0003);
913 }
914
915 #[test]
916 fn parse_extracts_linkage_type() {
917 let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x06];
918 let d = LinkageDescriptor::parse(&bytes).unwrap();
919 assert_eq!(d.linkage_type, LinkageType::DataBroadcastService);
920 }
921
922 #[test]
923 fn parse_none_type_preserves_private_data() {
924 let bytes = [
925 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB, 0xCC,
926 ];
927 let d = LinkageDescriptor::parse(&bytes).unwrap();
928 assert!(matches!(d.linkage_data, LinkageData::None));
929 assert_eq!(d.private_data, &[0xAA, 0xBB, 0xCC]);
930 }
931
932 #[test]
933 fn parse_accepts_empty_private_data() {
934 let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05];
935 let d = LinkageDescriptor::parse(&bytes).unwrap();
936 assert!(d.private_data.is_empty());
937 }
938
939 #[test]
940 fn parse_mobile_handover_with_initial_sid() {
941 let bytes = [
942 TAG, 0x0E, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x12, 0x00, 0x10, 0x00, 0x20, 0xDE, 0xAD, ];
952 let d = LinkageDescriptor::parse(&bytes).unwrap();
953 assert_eq!(d.linkage_type, LinkageType::MobileHandOver);
954 match &d.linkage_data {
955 LinkageData::MobileHandOver(m) => {
956 assert_eq!(
957 m.hand_over_type,
958 HandOverType::DvbIdenticalNeighbouringCountry
959 );
960 assert!(!m.origin_type);
961 assert_eq!(m.network_id, Some(0x0010));
962 assert_eq!(m.initial_service_id, Some(0x0020));
963 }
964 other => panic!("expected MobileHandOver, got {other:?}"),
965 }
966 assert_eq!(d.private_data, &[0xDE, 0xAD]);
967 }
968
969 #[test]
970 fn parse_mobile_handover_sdt_no_initial_sid() {
971 let bytes = [
972 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x2F, 0x00, 0x10, 0xCA, 0xFE, ];
981 let d = LinkageDescriptor::parse(&bytes).unwrap();
982 match &d.linkage_data {
983 LinkageData::MobileHandOver(m) => {
984 assert_eq!(m.hand_over_type, HandOverType::DvbLocalVariation);
985 assert!(m.origin_type);
986 assert_eq!(m.network_id, Some(0x0010));
987 assert_eq!(m.initial_service_id, None);
988 }
989 other => panic!("expected MobileHandOver, got {other:?}"),
990 }
991 assert_eq!(d.private_data, &[0xCA, 0xFE]);
992 }
993
994 #[test]
995 fn parse_event_linkage() {
996 let bytes = [
997 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0D, 0xAB, 0xCD, 0xC0, 0xBE, 0xEF, ];
1006 let d = LinkageDescriptor::parse(&bytes).unwrap();
1007 match &d.linkage_data {
1008 LinkageData::EventLinkage(e) => {
1009 assert_eq!(e.target_event_id, 0xABCD);
1010 assert!(e.target_listed);
1011 assert!(e.event_simulcast);
1012 }
1013 other => panic!("expected EventLinkage, got {other:?}"),
1014 }
1015 assert_eq!(d.private_data, &[0xBE, 0xEF]);
1016 }
1017
1018 #[test]
1019 fn parse_extended_event_linkage_user_defined() {
1020 let bytes = [
1021 TAG, 0x0E, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0E, 0x05, 0x12, 0x34, 0xCC, 0x56, 0x78, 0xCC, ];
1032 let d = LinkageDescriptor::parse(&bytes).unwrap();
1033 match &d.linkage_data {
1034 LinkageData::ExtendedEventLinkage(x) => {
1035 assert_eq!(x.entries.len(), 1);
1036 let e = &x.entries[0];
1037 assert_eq!(e.target_event_id, 0x1234);
1038 assert!(e.target_listed);
1039 assert!(e.event_simulcast);
1040 assert_eq!(e.link_type, LinkType::SdOrUhd);
1041 assert_eq!(e.target_id_type, TargetIdType::UseUserDefinedId);
1042 assert_eq!(
1043 e.target_id,
1044 TargetId::UserDefined {
1045 user_defined_id: 0x5678
1046 }
1047 );
1048 }
1049 other => panic!("expected ExtendedEventLinkage, got {other:?}"),
1050 }
1051 assert_eq!(d.private_data, &[0xCC]);
1052 }
1053
1054 #[test]
1055 fn parse_extended_event_linkage_dvb_target() {
1056 let bytes = [
1057 TAG, 0x0F, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0F, 0x07, 0xAA, 0xBB, 0x26, 0x00, 0x11, 0x00, 0x22, ];
1068 let d = LinkageDescriptor::parse(&bytes).unwrap();
1069 match &d.linkage_data {
1070 LinkageData::ExtendedEventLinkage(x) => {
1071 assert_eq!(x.entries.len(), 1);
1072 let e = &x.entries[0];
1073 assert_eq!(e.target_id_type, TargetIdType::UseTargetTransportStreamId);
1074 assert!(e.original_network_id_flag);
1075 assert!(!e.service_id_flag);
1076 assert_eq!(
1077 e.target_id,
1078 TargetId::Dvb {
1079 target_id_type: TargetIdType::UseTargetTransportStreamId,
1080 target_transport_stream_id: Some(0x0011),
1081 target_original_network_id: Some(0x0022),
1082 target_service_id: None,
1083 }
1084 );
1085 }
1086 other => panic!("expected ExtendedEventLinkage, got {other:?}"),
1087 }
1088 assert_eq!(d.private_data, &[] as &[u8]);
1089 }
1090
1091 #[test]
1092 fn parse_other_type_captures_raw_tail() {
1093 let bytes = [
1094 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x0B, 0xAA, 0xBB, 0xCC, ];
1101 let d = LinkageDescriptor::parse(&bytes).unwrap();
1102 match &d.linkage_data {
1103 LinkageData::Other(b) => assert_eq!(*b, &[0xAA, 0xBB, 0xCC]),
1104 other => panic!("expected Other, got {other:?}"),
1105 }
1106 assert!(d.private_data.is_empty());
1107 }
1108
1109 #[test]
1110 fn parse_user_defined_type_is_other() {
1111 let bytes = [
1112 TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x90, 0xFF, 0xFE, ];
1119 let d = LinkageDescriptor::parse(&bytes).unwrap();
1120 assert!(matches!(d.linkage_data, LinkageData::Other(_)));
1121 }
1122
1123 #[test]
1124 fn parse_rejects_wrong_tag() {
1125 let err = LinkageDescriptor::parse(&[0x4B, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05])
1126 .unwrap_err();
1127 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x4B, .. }));
1128 }
1129
1130 #[test]
1131 fn parse_rejects_body_shorter_than_seven() {
1132 let bytes = [TAG, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00];
1133 let err = LinkageDescriptor::parse(&bytes).unwrap_err();
1134 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
1135 }
1136
1137 #[test]
1138 fn parse_rejects_truncated_buffer() {
1139 let err = LinkageDescriptor::parse(&[TAG]).unwrap_err();
1140 assert!(matches!(err, Error::BufferTooShort { .. }));
1141 }
1142
1143 #[test]
1144 fn parse_rejects_truncated_mobile_handover() {
1145 let bytes = [
1146 TAG, 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x10, 0x00, 0x10, ];
1151 let err = LinkageDescriptor::parse(&bytes).unwrap_err();
1152 assert!(
1153 matches!(err, Error::InvalidDescriptor { .. }),
1154 "expected InvalidDescriptor for truncated mobile hand-over, got {err:?}"
1155 );
1156 }
1157
1158 #[test]
1159 fn serialize_round_trip_no_linkage_data() {
1160 let d = LinkageDescriptor {
1161 transport_stream_id: 0x1234,
1162 original_network_id: 0x5678,
1163 service_id: 0xABCD,
1164 linkage_type: LinkageType::EpgService,
1165 linkage_data: LinkageData::None,
1166 private_data: &[],
1167 };
1168 let mut buf = vec![0u8; d.serialized_len()];
1169 d.serialize_into(&mut buf).unwrap();
1170 let re = LinkageDescriptor::parse(&buf).unwrap();
1171 assert_eq!(d, re);
1172 }
1173
1174 #[test]
1175 fn serialize_round_trip_with_private_data() {
1176 let d = LinkageDescriptor {
1177 transport_stream_id: 0x0001,
1178 original_network_id: 0x0002,
1179 service_id: 0x0003,
1180 linkage_type: LinkageType::ServiceReplacementService,
1181 linkage_data: LinkageData::None,
1182 private_data: &[0xDE, 0xAD, 0xBE, 0xEF],
1183 };
1184 let mut buf = vec![0u8; d.serialized_len()];
1185 d.serialize_into(&mut buf).unwrap();
1186 let re = LinkageDescriptor::parse(&buf).unwrap();
1187 assert_eq!(d, re);
1188 }
1189
1190 #[test]
1191 fn serialize_round_trip_mobile_handover() {
1192 let d = LinkageDescriptor {
1193 transport_stream_id: 0x0001,
1194 original_network_id: 0x0002,
1195 service_id: 0x0003,
1196 linkage_type: LinkageType::MobileHandOver,
1197 linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
1198 hand_over_type: HandOverType::DvbAssociatedService,
1199 origin_type: false,
1200 network_id: Some(0x0044),
1201 initial_service_id: Some(0x0055),
1202 }),
1203 private_data: &[0xFF],
1204 };
1205 let mut buf = vec![0u8; d.serialized_len()];
1206 d.serialize_into(&mut buf).unwrap();
1207 let re = LinkageDescriptor::parse(&buf).unwrap();
1208 assert_eq!(d, re);
1209 }
1210
1211 #[test]
1212 fn serialize_round_trip_event_linkage() {
1213 let d = LinkageDescriptor {
1214 transport_stream_id: 0x0001,
1215 original_network_id: 0x0002,
1216 service_id: 0x0003,
1217 linkage_type: LinkageType::EventLinkage,
1218 linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
1219 target_event_id: 0x1234,
1220 target_listed: true,
1221 event_simulcast: false,
1222 }),
1223 private_data: &[],
1224 };
1225 let mut buf = vec![0u8; d.serialized_len()];
1226 d.serialize_into(&mut buf).unwrap();
1227 let re = LinkageDescriptor::parse(&buf).unwrap();
1228 assert_eq!(d, re);
1229 }
1230
1231 #[test]
1232 fn serialize_round_trip_extended_event_linkage() {
1233 let d = LinkageDescriptor {
1234 transport_stream_id: 0x0001,
1235 original_network_id: 0x0002,
1236 service_id: 0x0003,
1237 linkage_type: LinkageType::ExtendedEventLinkage(0x0E),
1238 linkage_data: LinkageData::ExtendedEventLinkage(ExtendedEventLinkageInfo {
1239 entries: vec![ExtendedEventLinkageEntry {
1240 target_event_id: 0xAAAA,
1241 target_listed: true,
1242 event_simulcast: true,
1243 link_type: LinkType::HdOrServiceFrameCompatible,
1244 target_id_type: TargetIdType::UseTargetTransportStreamId,
1245 original_network_id_flag: true,
1246 service_id_flag: true,
1247 target_id: TargetId::Dvb {
1248 target_id_type: TargetIdType::UseTargetTransportStreamId,
1249 target_transport_stream_id: Some(0x1111),
1250 target_original_network_id: Some(0x2222),
1251 target_service_id: Some(0x3333),
1252 },
1253 }],
1254 }),
1255 private_data: &[0xCC],
1256 };
1257 let mut buf = vec![0u8; d.serialized_len()];
1258 d.serialize_into(&mut buf).unwrap();
1259 let re = LinkageDescriptor::parse(&buf).unwrap();
1260 assert_eq!(d, re);
1261 }
1262
1263 #[test]
1264 fn serialize_round_trip_other() {
1265 let raw = [0xAA, 0xBB, 0xCC];
1266 let d = LinkageDescriptor {
1267 transport_stream_id: 0x0001,
1268 original_network_id: 0x0002,
1269 service_id: 0x0003,
1270 linkage_type: LinkageType::IpMacNotificationService,
1271 linkage_data: LinkageData::Other(&raw),
1272 private_data: &[],
1273 };
1274 let mut buf = vec![0u8; d.serialized_len()];
1275 d.serialize_into(&mut buf).unwrap();
1276 let re = LinkageDescriptor::parse(&buf).unwrap();
1277 assert_eq!(d, re);
1278 }
1279
1280 #[test]
1281 fn serialize_reserved_bits_are_set() {
1282 let d = LinkageDescriptor {
1283 transport_stream_id: 0x0001,
1284 original_network_id: 0x0002,
1285 service_id: 0x0003,
1286 linkage_type: LinkageType::EventLinkage,
1287 linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
1288 target_event_id: 0x0000,
1289 target_listed: false,
1290 event_simulcast: false,
1291 }),
1292 private_data: &[],
1293 };
1294 let mut buf = vec![0u8; d.serialized_len()];
1295 d.serialize_into(&mut buf).unwrap();
1296 assert_eq!(buf[11] & RESERVED_EVENT_MASK, RESERVED_EVENT_MASK);
1297
1298 let d2 = LinkageDescriptor {
1299 transport_stream_id: 0x0001,
1300 original_network_id: 0x0002,
1301 service_id: 0x0003,
1302 linkage_type: LinkageType::MobileHandOver,
1303 linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
1304 hand_over_type: HandOverType::Reserved(0),
1305 origin_type: true,
1306 network_id: None,
1307 initial_service_id: None,
1308 }),
1309 private_data: &[],
1310 };
1311 let mut buf2 = vec![0u8; d2.serialized_len()];
1312 d2.serialize_into(&mut buf2).unwrap();
1313 assert_eq!(buf2[9] & RESERVED_HANDOVER_MASK, RESERVED_HANDOVER_MASK);
1314 }
1315
1316 #[test]
1317 fn parse_mobile_handover_type4_no_network_id() {
1318 let bytes = [
1319 TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x4E, 0x00, 0x20, ];
1327 let d = LinkageDescriptor::parse(&bytes).unwrap();
1328 match &d.linkage_data {
1329 LinkageData::MobileHandOver(m) => {
1330 assert_eq!(m.hand_over_type, HandOverType::Reserved(4));
1331 assert!(!m.origin_type);
1332 assert_eq!(m.network_id, None);
1333 assert_eq!(m.initial_service_id, Some(0x0020));
1334 }
1335 other => panic!("expected MobileHandOver, got {other:?}"),
1336 }
1337 }
1338
1339 #[test]
1340 fn parse_mobile_handover_type1_network_id_present() {
1341 let bytes = [
1342 TAG, 0x0C, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, 0x1F, 0x00, 0x10, 0xCA, 0xFE, ];
1351 let d = LinkageDescriptor::parse(&bytes).unwrap();
1352 match &d.linkage_data {
1353 LinkageData::MobileHandOver(m) => {
1354 assert_eq!(
1355 m.hand_over_type,
1356 HandOverType::DvbIdenticalNeighbouringCountry
1357 );
1358 assert!(m.origin_type);
1359 assert_eq!(m.network_id, Some(0x0010));
1360 assert_eq!(m.initial_service_id, None);
1361 }
1362 other => panic!("expected MobileHandOver, got {other:?}"),
1363 }
1364 assert_eq!(d.private_data, &[0xCA, 0xFE]);
1365 }
1366
1367 #[test]
1368 fn linkage_type_full_range_round_trip() {
1369 for b in 0..=0xFF_u8 {
1370 let lt = LinkageType::from_u8(b);
1371 assert_eq!(lt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1372 }
1373 }
1374
1375 #[test]
1376 fn hand_over_type_full_range_round_trip() {
1377 for b in 0..=0xFF_u8 {
1378 let ht = HandOverType::from_u8(b);
1379 assert_eq!(ht.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1380 }
1381 }
1382
1383 #[test]
1384 fn link_type_full_range_round_trip() {
1385 for b in 0..=0xFF_u8 {
1386 let lt = LinkType::from_u8(b);
1387 assert_eq!(lt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1388 }
1389 }
1390
1391 #[test]
1392 fn target_id_type_full_range_round_trip() {
1393 for b in 0..=0xFF_u8 {
1394 let tt = TargetIdType::from_u8(b);
1395 assert_eq!(tt.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
1396 }
1397 }
1398}