Skip to main content

dvb_si/descriptors/
linkage.rs

1//! Linkage Descriptor — ETSI EN 300 468 §6.2.19 (tag 0x4A).
2//!
3//! Carried inside NIT (network linkage), BAT (bouquet linkage) and
4//! SDT (service replacement / premiere hand-over).
5//!
6//! Conditional inner structures (§6.2.19.2–4):
7//! - `linkage_type == 0x08` → [`MobileHandOverInfo`] (Table 61)
8//! - `linkage_type == 0x0D` → [`EventLinkageInfo`]    (Table 64)
9//! - `linkage_type 0x0E..=0x1F` → [`ExtendedEventLinkageInfo`] (Table 65)
10//!
11//! Other linkage types (including user-defined `0x80..=0xFE` and those
12//! defined in companion specs such as TS 102 006 / EN 301 192 / EN 303 560)
13//! have no EN 300 468 conditional block; their bytes after the fixed fields
14//! are the `private_data_byte` tail.
15
16use super::descriptor_body;
17use crate::error::{Error, Result};
18use dvb_common::{Parse, Serialize};
19
20/// Descriptor tag for linkage_descriptor.
21pub 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/// Mobile hand-over info — EN 300 468 Table 61 (`linkage_type == 0x08`).
41#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize))]
43pub struct MobileHandOverInfo {
44    /// hand_over_type — 4 bits `[7:4]`.  See Table 62.
45    pub hand_over_type: u8,
46    /// origin_type — 1 bit `[0]`.  `false` = NIT, `true` = SDT (Table 63).
47    pub origin_type: bool,
48    /// network_id — 16 bits, present only when hand_over_type in {1, 2, 3}.
49    pub network_id: Option<u16>,
50    /// initial_service_id — present only when `origin_type == false` (NIT).
51    pub initial_service_id: Option<u16>,
52}
53
54impl MobileHandOverInfo {
55    fn serialized_len(&self) -> usize {
56        1 + self.network_id.map_or(0, |_| 2) + self.initial_service_id.map_or(0, |_| 2)
57    }
58
59    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
60        let len = self.serialized_len();
61        if buf.len() < len {
62            return Err(Error::OutputBufferTooSmall {
63                need: len,
64                have: buf.len(),
65            });
66        }
67        let flags_byte =
68            (self.hand_over_type << 4) | RESERVED_HANDOVER_MASK | u8::from(self.origin_type);
69        buf[0] = flags_byte;
70        let mut pos = 1;
71        if let Some(nid) = self.network_id {
72            buf[pos..pos + 2].copy_from_slice(&nid.to_be_bytes());
73            pos += 2;
74        }
75        if let Some(sid) = self.initial_service_id {
76            buf[pos..pos + 2].copy_from_slice(&sid.to_be_bytes());
77        }
78        Ok(len)
79    }
80}
81
82/// Event linkage info — EN 300 468 Table 64 (`linkage_type == 0x0D`).
83#[derive(Debug, Clone, PartialEq, Eq)]
84#[cfg_attr(feature = "serde", derive(serde::Serialize))]
85pub struct EventLinkageInfo {
86    /// target_event_id — 16 bits.
87    pub target_event_id: u16,
88    /// target_listed — 1 bit `[7]`.
89    pub target_listed: bool,
90    /// event_simulcast — 1 bit `[6]`.
91    pub event_simulcast: bool,
92}
93
94impl EventLinkageInfo {
95    const SERIALIZED_LEN: usize = 3;
96
97    fn serialized_len(&self) -> usize {
98        Self::SERIALIZED_LEN
99    }
100
101    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
102        if buf.len() < Self::SERIALIZED_LEN {
103            return Err(Error::OutputBufferTooSmall {
104                need: Self::SERIALIZED_LEN,
105                have: buf.len(),
106            });
107        }
108        buf[0..2].copy_from_slice(&self.target_event_id.to_be_bytes());
109        let mut byte2: u8 = RESERVED_EVENT_MASK;
110        if self.target_listed {
111            byte2 |= TARGET_LISTED_MASK;
112        }
113        if self.event_simulcast {
114            byte2 |= EVENT_SIMULCAST_MASK;
115        }
116        buf[2] = byte2;
117        Ok(Self::SERIALIZED_LEN)
118    }
119}
120
121/// Target identification — EN 300 468 Table 65 inner conditional fields.
122#[derive(Debug, Clone, PartialEq, Eq)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize))]
124pub enum TargetId {
125    /// `target_id_type == 3` — user_defined_id (16 bits).
126    UserDefined {
127        /// User-defined target service identifier.
128        user_defined_id: u16,
129    },
130    /// `target_id_type != 3` — DVB addressing with optional sub-fields.
131    Dvb {
132        /// `target_id_type` value (0, 1, or 2).  See Table 67.
133        target_id_type: u8,
134        /// target_transport_stream_id — present when `target_id_type == 1`.
135        target_transport_stream_id: Option<u16>,
136        /// target_original_network_id — present when `original_network_id_flag == 1`.
137        target_original_network_id: Option<u16>,
138        /// target_service_id — present when `service_id_flag == 1`.
139        target_service_id: Option<u16>,
140    },
141}
142
143impl TargetId {
144    fn serialized_len(&self) -> usize {
145        match self {
146            TargetId::UserDefined { .. } => 2,
147            TargetId::Dvb {
148                target_transport_stream_id,
149                target_original_network_id,
150                target_service_id,
151                ..
152            } => {
153                usize::from(target_transport_stream_id.is_some()) * 2
154                    + usize::from(target_original_network_id.is_some()) * 2
155                    + usize::from(target_service_id.is_some()) * 2
156            }
157        }
158    }
159
160    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
161        let len = self.serialized_len();
162        if buf.len() < len {
163            return Err(Error::OutputBufferTooSmall {
164                need: len,
165                have: buf.len(),
166            });
167        }
168        match self {
169            TargetId::UserDefined { user_defined_id } => {
170                buf[..2].copy_from_slice(&user_defined_id.to_be_bytes());
171            }
172            TargetId::Dvb {
173                target_transport_stream_id,
174                target_original_network_id,
175                target_service_id,
176                ..
177            } => {
178                let ts_len = target_transport_stream_id.map_or(0, |_| 2);
179                let onid_len = target_original_network_id.map_or(0, |_| 2);
180                if let Some(ts_id) = target_transport_stream_id {
181                    buf[..2].copy_from_slice(&ts_id.to_be_bytes());
182                }
183                if let Some(onid) = target_original_network_id {
184                    buf[ts_len..ts_len + 2].copy_from_slice(&onid.to_be_bytes());
185                }
186                if let Some(sid) = target_service_id {
187                    let off = ts_len + onid_len;
188                    buf[off..off + 2].copy_from_slice(&sid.to_be_bytes());
189                }
190            }
191        }
192        Ok(len)
193    }
194}
195
196/// One entry in the extended event linkage loop — EN 300 468 Table 65.
197#[derive(Debug, Clone, PartialEq, Eq)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize))]
199pub struct ExtendedEventLinkageEntry {
200    /// target_event_id — 16 bits.
201    pub target_event_id: u16,
202    /// target_listed — 1 bit `[7]`.
203    pub target_listed: bool,
204    /// event_simulcast — 1 bit `[6]`.
205    pub event_simulcast: bool,
206    /// link_type — 2 bits `[5:4]`.  See Table 66.
207    pub link_type: u8,
208    /// target_id_type — 2 bits `[3:2]`.  See Table 67.
209    pub target_id_type: u8,
210    /// original_network_id_flag — 1 bit `[1]`.
211    pub original_network_id_flag: bool,
212    /// service_id_flag — 1 bit `[0]`.
213    pub service_id_flag: bool,
214    /// Conditional target identification.
215    pub target_id: TargetId,
216}
217
218impl ExtendedEventLinkageEntry {
219    fn serialized_len(&self) -> usize {
220        2 + 1 + self.target_id.serialized_len()
221    }
222
223    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
224        let flags_byte_len = 3;
225        if buf.len() < flags_byte_len {
226            return Err(Error::OutputBufferTooSmall {
227                need: flags_byte_len,
228                have: buf.len(),
229            });
230        }
231        buf[0..2].copy_from_slice(&self.target_event_id.to_be_bytes());
232        let mut byte2: u8 = 0;
233        if self.target_listed {
234            byte2 |= EXT_TARGET_LISTED_MASK;
235        }
236        if self.event_simulcast {
237            byte2 |= EXT_EVENT_SIMULCAST_MASK;
238        }
239        byte2 |= (self.link_type & 0x03) << 4;
240        byte2 |= (self.target_id_type & 0x03) << 2;
241        if self.original_network_id_flag {
242            byte2 |= EXT_ONID_FLAG_MASK;
243        }
244        if self.service_id_flag {
245            byte2 |= EXT_SID_FLAG_MASK;
246        }
247        buf[2] = byte2;
248        let tid_written = self.target_id.serialize_into(&mut buf[3..])?;
249        Ok(3 + tid_written)
250    }
251}
252
253/// Extended event linkage info — EN 300 468 Table 65
254/// (`linkage_type 0x0E..=0x1F`).
255#[derive(Debug, Clone, PartialEq, Eq)]
256#[cfg_attr(feature = "serde", derive(serde::Serialize))]
257pub struct ExtendedEventLinkageInfo {
258    /// Entries in the extended event linkage loop.
259    pub entries: Vec<ExtendedEventLinkageEntry>,
260}
261
262impl ExtendedEventLinkageInfo {
263    fn serialized_len(&self) -> usize {
264        1 + self
265            .entries
266            .iter()
267            .map(|e| e.serialized_len())
268            .sum::<usize>()
269    }
270
271    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
272        let loop_len: usize = self.entries.iter().map(|e| e.serialized_len()).sum();
273        let total = 1 + loop_len;
274        if buf.len() < total {
275            return Err(Error::OutputBufferTooSmall {
276                need: total,
277                have: buf.len(),
278            });
279        }
280        buf[0] = loop_len as u8;
281        let mut pos = 1;
282        for entry in &self.entries {
283            let written = entry.serialize_into(&mut buf[pos..])?;
284            pos += written;
285        }
286        Ok(total)
287    }
288}
289
290/// Linkage-type-conditional inner data — EN 300 468 §6.2.19.2–4.
291///
292/// Typed variants correspond to the conditional blocks defined in EN 300 468;
293/// `None` covers all other linkage types (no conditional block per the main
294/// spec, remaining bytes go to `private_data`); `Other` captures the raw tail
295/// for linkage types whose conditional structure is defined in companion specs
296/// (e.g. TS 102 006 linkage_type 0x09/0x0A, EN 301 192 0x0B/0x0C,
297/// EN 303 560 0x20) or user-defined types (`0x80..=0xFE`) where we cannot
298/// distinguish the conditional block from the `private_data_byte` loop.
299#[derive(Debug, Clone, PartialEq, Eq)]
300#[non_exhaustive]
301#[cfg_attr(feature = "serde", derive(serde::Serialize))]
302pub enum LinkageData<'a> {
303    /// `linkage_type == 0x08` — mobile hand-over info (Table 61).
304    MobileHandOver(MobileHandOverInfo),
305    /// `linkage_type == 0x0D` — event linkage info (Table 64).
306    EventLinkage(EventLinkageInfo),
307    /// `linkage_type 0x0E..=0x1F` — extended event linkage info (Table 65).
308    ExtendedEventLinkage(ExtendedEventLinkageInfo),
309    /// No EN 300 468 conditional block — `private_data` starts immediately
310    /// after the fixed fields.
311    None,
312    /// Raw tail for linkage types with externally-defined or user-defined
313    /// conditional structure.  Because we cannot determine the boundary
314    /// between the conditional block and the `private_data_byte` loop, all
315    /// remaining bytes are captured here and `private_data` is empty.
316    #[cfg_attr(feature = "serde", serde(borrow))]
317    Other(&'a [u8]),
318}
319
320impl LinkageData<'_> {
321    fn serialized_len(&self) -> usize {
322        match self {
323            LinkageData::MobileHandOver(m) => m.serialized_len(),
324            LinkageData::EventLinkage(e) => e.serialized_len(),
325            LinkageData::ExtendedEventLinkage(x) => x.serialized_len(),
326            LinkageData::None => 0,
327            LinkageData::Other(b) => b.len(),
328        }
329    }
330
331    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
332        match self {
333            LinkageData::MobileHandOver(m) => m.serialize_into(buf),
334            LinkageData::EventLinkage(e) => e.serialize_into(buf),
335            LinkageData::ExtendedEventLinkage(x) => x.serialize_into(buf),
336            LinkageData::None => Ok(0),
337            LinkageData::Other(b) => {
338                if buf.len() < b.len() {
339                    return Err(Error::OutputBufferTooSmall {
340                        need: b.len(),
341                        have: buf.len(),
342                    });
343                }
344                buf[..b.len()].copy_from_slice(b);
345                Ok(b.len())
346            }
347        }
348    }
349}
350
351fn parse_mobile_handover(bytes: &[u8], end: usize) -> Result<MobileHandOverInfo> {
352    if end < 1 {
353        return Err(Error::InvalidDescriptor {
354            tag: TAG,
355            reason: "mobile hand-over info needs at least flags byte",
356        });
357    }
358    let flags_byte = bytes[0];
359    let hand_over_type = (flags_byte & HANDOVER_TYPE_MASK) >> 4;
360    let origin_type = (flags_byte & ORIGIN_TYPE_MASK) != 0;
361    let mut pos = 1;
362    let network_id = if matches!(hand_over_type, 0x01..=0x03) {
363        if pos + 2 > end {
364            return Err(Error::InvalidDescriptor {
365                tag: TAG,
366                reason: "mobile hand-over info with gated network_id needs at least 3 bytes",
367            });
368        }
369        let nid = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
370        pos += 2;
371        Some(nid)
372    } else {
373        None
374    };
375    let initial_service_id = if !origin_type {
376        if pos + 2 > end {
377            return Err(Error::InvalidDescriptor {
378                tag: TAG,
379                reason: "mobile hand-over info with origin_type=NIT needs initial_service_id",
380            });
381        }
382        Some(u16::from_be_bytes([bytes[pos], bytes[pos + 1]]))
383    } else {
384        None
385    };
386    Ok(MobileHandOverInfo {
387        hand_over_type,
388        origin_type,
389        network_id,
390        initial_service_id,
391    })
392}
393
394fn parse_event_linkage(bytes: &[u8]) -> Result<EventLinkageInfo> {
395    if bytes.len() < 3 {
396        return Err(Error::InvalidDescriptor {
397            tag: TAG,
398            reason: "event linkage info needs 3 bytes",
399        });
400    }
401    let target_event_id = u16::from_be_bytes([bytes[0], bytes[1]]);
402    let target_listed = (bytes[2] & TARGET_LISTED_MASK) != 0;
403    let event_simulcast = (bytes[2] & EVENT_SIMULCAST_MASK) != 0;
404    Ok(EventLinkageInfo {
405        target_event_id,
406        target_listed,
407        event_simulcast,
408    })
409}
410
411fn parse_extended_event_linkage(bytes: &[u8]) -> Result<ExtendedEventLinkageInfo> {
412    if bytes.is_empty() {
413        return Err(Error::InvalidDescriptor {
414            tag: TAG,
415            reason: "extended event linkage info needs at least loop_length byte",
416        });
417    }
418    let loop_length = bytes[0] as usize;
419    let loop_end = 1 + loop_length;
420    if bytes.len() < loop_end {
421        return Err(Error::BufferTooShort {
422            need: loop_end,
423            have: bytes.len(),
424            what: "extended event linkage info loop",
425        });
426    }
427    let mut entries = Vec::new();
428    let mut pos = 1;
429    let read_u16 = |p: &mut usize| -> Result<u16> {
430        if *p + 2 > loop_end {
431            return Err(Error::InvalidDescriptor {
432                tag: TAG,
433                reason: "extended event linkage entry truncated (need u16)",
434            });
435        }
436        let v = u16::from_be_bytes([bytes[*p], bytes[*p + 1]]);
437        *p += 2;
438        Ok(v)
439    };
440    while pos < loop_end {
441        let target_event_id = read_u16(&mut pos)?;
442        if pos >= loop_end {
443            return Err(Error::InvalidDescriptor {
444                tag: TAG,
445                reason: "extended event linkage entry truncated (need flags byte)",
446            });
447        }
448        let fb = bytes[pos];
449        pos += 1;
450        let target_listed = (fb & EXT_TARGET_LISTED_MASK) != 0;
451        let event_simulcast = (fb & EXT_EVENT_SIMULCAST_MASK) != 0;
452        let link_type = (fb & EXT_LINK_TYPE_MASK) >> 4;
453        let target_id_type = (fb & EXT_TARGET_ID_TYPE_MASK) >> 2;
454        let original_network_id_flag = (fb & EXT_ONID_FLAG_MASK) != 0;
455        let service_id_flag = (fb & EXT_SID_FLAG_MASK) != 0;
456
457        let target_id = if target_id_type == 3 {
458            let user_defined_id = read_u16(&mut pos)?;
459            TargetId::UserDefined { user_defined_id }
460        } else {
461            let target_transport_stream_id = if target_id_type == 1 {
462                Some(read_u16(&mut pos)?)
463            } else {
464                None
465            };
466            let target_original_network_id = if original_network_id_flag {
467                Some(read_u16(&mut pos)?)
468            } else {
469                None
470            };
471            let target_service_id = if service_id_flag {
472                Some(read_u16(&mut pos)?)
473            } else {
474                None
475            };
476            TargetId::Dvb {
477                target_id_type,
478                target_transport_stream_id,
479                target_original_network_id,
480                target_service_id,
481            }
482        };
483        entries.push(ExtendedEventLinkageEntry {
484            target_event_id,
485            target_listed,
486            event_simulcast,
487            link_type,
488            target_id_type,
489            original_network_id_flag,
490            service_id_flag,
491            target_id,
492        });
493    }
494    Ok(ExtendedEventLinkageInfo { entries })
495}
496
497/// Linkage Descriptor.
498#[derive(Debug, Clone, PartialEq, Eq)]
499#[cfg_attr(feature = "serde", derive(serde::Serialize))]
500#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
501pub struct LinkageDescriptor<'a> {
502    /// transport_stream_id of the linked-to TS.
503    pub transport_stream_id: u16,
504    /// original_network_id of the linked-to TS.
505    pub original_network_id: u16,
506    /// service_id of the linked-to service (0 if linkage is at the network or
507    /// bouquet level).
508    pub service_id: u16,
509    /// linkage_type byte (Table 60): 0x01 information, 0x02 EPG,
510    /// 0x03 CA_replacement, 0x04 TS_containing_complete_SI, 0x05
511    /// service_replacement, 0x06 data_broadcast, 0x07 RCS_map,
512    /// 0x08 mobile_hand-over, 0x09 SSU, 0x0A SSU BAT/NIT,
513    /// 0x0B IP/MAC notification, 0x0C INT BAT/NIT, 0x0D event_linkage,
514    /// 0x0E..=0x1F extended_event_linkage, 0x20 downloadable font,
515    /// 0x21 Native IP bootstrap, 0x80..=0xFE user defined.
516    pub linkage_type: u8,
517    /// Linkage-type-conditional inner structure.
518    pub linkage_data: LinkageData<'a>,
519    /// Trailing `private_data_byte` run — bytes after the conditional block
520    /// (if any).  Empty when `linkage_data` is `Other`.
521    pub private_data: &'a [u8],
522}
523
524const LINKAGE_TYPES_WITH_OTHER: &[u8] = &[0x09, 0x0A, 0x0B, 0x0C, 0x20, 0x21];
525
526impl<'a> Parse<'a> for LinkageDescriptor<'a> {
527    type Error = crate::error::Error;
528    fn parse(bytes: &'a [u8]) -> Result<Self> {
529        let body = descriptor_body(
530            bytes,
531            TAG,
532            "LinkageDescriptor",
533            "unexpected tag for linkage_descriptor",
534        )?;
535        if body.len() < FIXED_FIELDS_LEN {
536            return Err(Error::InvalidDescriptor {
537                tag: TAG,
538                reason: "linkage_descriptor body shorter than minimum 7 bytes",
539            });
540        }
541        let transport_stream_id = u16::from_be_bytes([body[0], body[1]]);
542        let original_network_id = u16::from_be_bytes([body[2], body[3]]);
543        let service_id = u16::from_be_bytes([body[4], body[5]]);
544        let linkage_type = body[6];
545        let tail = &body[FIXED_FIELDS_LEN..];
546        let tail_len = tail.len();
547
548        let (linkage_data, private_data) = match linkage_type {
549            0x08 => {
550                let info = parse_mobile_handover(tail, tail_len)?;
551                let consumed = info.serialized_len();
552                (LinkageData::MobileHandOver(info), &tail[consumed..])
553            }
554            0x0D => {
555                let info = parse_event_linkage(tail)?;
556                let consumed = EventLinkageInfo::SERIALIZED_LEN;
557                (LinkageData::EventLinkage(info), &tail[consumed..])
558            }
559            0x0E..=0x1F => {
560                let info = parse_extended_event_linkage(tail)?;
561                let consumed = info.serialized_len();
562                (LinkageData::ExtendedEventLinkage(info), &tail[consumed..])
563            }
564            lt if LINKAGE_TYPES_WITH_OTHER.contains(&lt) || (0x80..=0xFE).contains(&lt) => {
565                (LinkageData::Other(tail), &[] as &[u8])
566            }
567            _ => (LinkageData::None, tail),
568        };
569        Ok(Self {
570            transport_stream_id,
571            original_network_id,
572            service_id,
573            linkage_type,
574            linkage_data,
575            private_data,
576        })
577    }
578}
579
580impl Serialize for LinkageDescriptor<'_> {
581    type Error = crate::error::Error;
582    fn serialized_len(&self) -> usize {
583        HEADER_LEN + FIXED_FIELDS_LEN + self.linkage_data.serialized_len() + self.private_data.len()
584    }
585
586    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
587        let len = self.serialized_len();
588        if buf.len() < len {
589            return Err(Error::OutputBufferTooSmall {
590                need: len,
591                have: buf.len(),
592            });
593        }
594        buf[0] = TAG;
595        buf[1] = (len - HEADER_LEN) as u8;
596        let bs = HEADER_LEN;
597        buf[bs..bs + 2].copy_from_slice(&self.transport_stream_id.to_be_bytes());
598        buf[bs + 2..bs + 4].copy_from_slice(&self.original_network_id.to_be_bytes());
599        buf[bs + 4..bs + 6].copy_from_slice(&self.service_id.to_be_bytes());
600        buf[bs + 6] = self.linkage_type;
601        let ld_start = bs + FIXED_FIELDS_LEN;
602        let ld_written = self.linkage_data.serialize_into(&mut buf[ld_start..])?;
603        let pd_start = ld_start + ld_written;
604        if !self.private_data.is_empty() {
605            buf[pd_start..pd_start + self.private_data.len()].copy_from_slice(self.private_data);
606        }
607        Ok(len)
608    }
609}
610impl<'a> crate::traits::DescriptorDef<'a> for LinkageDescriptor<'a> {
611    const TAG: u8 = TAG;
612    const NAME: &'static str = "LINKAGE";
613}
614
615#[cfg(test)]
616mod tests {
617    use super::*;
618
619    #[test]
620    fn parse_extracts_tsid_onid_sid() {
621        let bytes = [
622            TAG, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB,
623        ];
624        let d = LinkageDescriptor::parse(&bytes).unwrap();
625        assert_eq!(d.transport_stream_id, 0x0001);
626        assert_eq!(d.original_network_id, 0x0002);
627        assert_eq!(d.service_id, 0x0003);
628    }
629
630    #[test]
631    fn parse_extracts_linkage_type() {
632        let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x06];
633        let d = LinkageDescriptor::parse(&bytes).unwrap();
634        assert_eq!(d.linkage_type, 0x06);
635    }
636
637    #[test]
638    fn parse_none_type_preserves_private_data() {
639        let bytes = [
640            TAG, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05, 0xAA, 0xBB, 0xCC,
641        ];
642        let d = LinkageDescriptor::parse(&bytes).unwrap();
643        assert!(matches!(d.linkage_data, LinkageData::None));
644        assert_eq!(d.private_data, &[0xAA, 0xBB, 0xCC]);
645    }
646
647    #[test]
648    fn parse_accepts_empty_private_data() {
649        let bytes = [TAG, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05];
650        let d = LinkageDescriptor::parse(&bytes).unwrap();
651        assert!(d.private_data.is_empty());
652    }
653
654    #[test]
655    fn parse_mobile_handover_with_initial_sid() {
656        let bytes = [
657            TAG, 0x0E, // length 14 = 7 fixed + 5 handover + 2 priv
658            0x00, 0x01, // ts_id
659            0x00, 0x02, // onid
660            0x00, 0x03, // sid
661            0x08, // linkage_type = mobile hand-over
662            0x12, // hand_over_type=1, rfu=110, origin_type=0 (NIT)
663            0x00, 0x10, // network_id
664            0x00, 0x20, // initial_service_id
665            0xDE, 0xAD, // private_data
666        ];
667        let d = LinkageDescriptor::parse(&bytes).unwrap();
668        assert_eq!(d.linkage_type, 0x08);
669        match &d.linkage_data {
670            LinkageData::MobileHandOver(m) => {
671                assert_eq!(m.hand_over_type, 1);
672                assert!(!m.origin_type);
673                assert_eq!(m.network_id, Some(0x0010));
674                assert_eq!(m.initial_service_id, Some(0x0020));
675            }
676            other => panic!("expected MobileHandOver, got {other:?}"),
677        }
678        assert_eq!(d.private_data, &[0xDE, 0xAD]);
679    }
680
681    #[test]
682    fn parse_mobile_handover_sdt_no_initial_sid() {
683        let bytes = [
684            TAG, 0x0C, // length 12 = 7 fixed + 3 handover + 2 priv
685            0x00, 0x01, // ts_id
686            0x00, 0x02, // onid
687            0x00, 0x03, // sid
688            0x08, // linkage_type = mobile hand-over
689            0x2F, // hand_over_type=2, rfu=111, origin_type=1 (SDT)
690            0x00, 0x10, // network_id
691            0xCA, 0xFE, // private_data
692        ];
693        let d = LinkageDescriptor::parse(&bytes).unwrap();
694        match &d.linkage_data {
695            LinkageData::MobileHandOver(m) => {
696                assert_eq!(m.hand_over_type, 2);
697                assert!(m.origin_type);
698                assert_eq!(m.network_id, Some(0x0010));
699                assert_eq!(m.initial_service_id, None);
700            }
701            other => panic!("expected MobileHandOver, got {other:?}"),
702        }
703        assert_eq!(d.private_data, &[0xCA, 0xFE]);
704    }
705
706    #[test]
707    fn parse_event_linkage() {
708        let bytes = [
709            TAG, 0x0C, // length 12 = 7 fixed + 3 event + 2 priv
710            0x00, 0x01, // ts_id
711            0x00, 0x02, // onid
712            0x00, 0x03, // sid
713            0x0D, // linkage_type = event linkage
714            0xAB, 0xCD, // target_event_id
715            0xC0, // target_listed=1, event_simulcast=1, rfu=000000
716            0xBE, 0xEF, // private_data
717        ];
718        let d = LinkageDescriptor::parse(&bytes).unwrap();
719        match &d.linkage_data {
720            LinkageData::EventLinkage(e) => {
721                assert_eq!(e.target_event_id, 0xABCD);
722                assert!(e.target_listed);
723                assert!(e.event_simulcast);
724            }
725            other => panic!("expected EventLinkage, got {other:?}"),
726        }
727        assert_eq!(d.private_data, &[0xBE, 0xEF]);
728    }
729
730    #[test]
731    fn parse_extended_event_linkage_user_defined() {
732        let bytes = [
733            TAG, 0x0E, // length 14 = 7 fixed + 6 ext + 1 priv
734            0x00, 0x01, // ts_id
735            0x00, 0x02, // onid
736            0x00, 0x03, // sid
737            0x0E, // linkage_type = extended event linkage
738            0x05, // loop_length = 5
739            0x12, 0x34, // target_event_id
740            0xCC, // target_listed=1, event_simulcast=1, link_type=0, target_id_type=3, flags=0
741            0x56, 0x78, // user_defined_id
742            0xCC, // private_data
743        ];
744        let d = LinkageDescriptor::parse(&bytes).unwrap();
745        match &d.linkage_data {
746            LinkageData::ExtendedEventLinkage(x) => {
747                assert_eq!(x.entries.len(), 1);
748                let e = &x.entries[0];
749                assert_eq!(e.target_event_id, 0x1234);
750                assert!(e.target_listed);
751                assert!(e.event_simulcast);
752                assert_eq!(e.link_type, 0);
753                assert_eq!(e.target_id_type, 3);
754                assert_eq!(
755                    e.target_id,
756                    TargetId::UserDefined {
757                        user_defined_id: 0x5678
758                    }
759                );
760            }
761            other => panic!("expected ExtendedEventLinkage, got {other:?}"),
762        }
763        assert_eq!(d.private_data, &[0xCC]);
764    }
765
766    #[test]
767    fn parse_extended_event_linkage_dvb_target() {
768        let bytes = [
769            TAG, 0x0F, // length 15 = 7 fixed + 8 ext
770            0x00, 0x01, // ts_id
771            0x00, 0x02, // onid
772            0x00, 0x03, // sid
773            0x0F, // linkage_type
774            0x07, // loop_length = 7
775            0xAA, 0xBB, // target_event_id
776            0x26, // target_listed=0, event_simulcast=0, link_type=2, target_id_type=1, onid_flag=1, sid_flag=0
777            0x00, 0x11, // target_transport_stream_id (target_id_type=1)
778            0x00, 0x22, // target_original_network_id (onid_flag=1)
779        ];
780        let d = LinkageDescriptor::parse(&bytes).unwrap();
781        match &d.linkage_data {
782            LinkageData::ExtendedEventLinkage(x) => {
783                assert_eq!(x.entries.len(), 1);
784                let e = &x.entries[0];
785                assert_eq!(e.target_id_type, 1);
786                assert!(e.original_network_id_flag);
787                assert!(!e.service_id_flag);
788                assert_eq!(
789                    e.target_id,
790                    TargetId::Dvb {
791                        target_id_type: 1,
792                        target_transport_stream_id: Some(0x0011),
793                        target_original_network_id: Some(0x0022),
794                        target_service_id: None,
795                    }
796                );
797            }
798            other => panic!("expected ExtendedEventLinkage, got {other:?}"),
799        }
800        assert_eq!(d.private_data, &[] as &[u8]);
801    }
802
803    #[test]
804    fn parse_other_type_captures_raw_tail() {
805        let bytes = [
806            TAG, 0x0A, // length 10
807            0x00, 0x01, // ts_id
808            0x00, 0x02, // onid
809            0x00, 0x03, // sid
810            0x0B, // linkage_type = IP/MAC notification (EN 301 192)
811            0xAA, 0xBB, 0xCC, // raw tail
812        ];
813        let d = LinkageDescriptor::parse(&bytes).unwrap();
814        match &d.linkage_data {
815            LinkageData::Other(b) => assert_eq!(*b, &[0xAA, 0xBB, 0xCC]),
816            other => panic!("expected Other, got {other:?}"),
817        }
818        assert!(d.private_data.is_empty());
819    }
820
821    #[test]
822    fn parse_user_defined_type_is_other() {
823        let bytes = [
824            TAG, 0x09, // length 9
825            0x00, 0x01, // ts_id
826            0x00, 0x02, // onid
827            0x00, 0x03, // sid
828            0x90, // user-defined linkage_type
829            0xFF, 0xFE, // raw tail
830        ];
831        let d = LinkageDescriptor::parse(&bytes).unwrap();
832        assert!(matches!(d.linkage_data, LinkageData::Other(_)));
833    }
834
835    #[test]
836    fn parse_rejects_wrong_tag() {
837        let err = LinkageDescriptor::parse(&[0x4B, 0x07, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x05])
838            .unwrap_err();
839        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x4B, .. }));
840    }
841
842    #[test]
843    fn parse_rejects_body_shorter_than_seven() {
844        let bytes = [TAG, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00];
845        let err = LinkageDescriptor::parse(&bytes).unwrap_err();
846        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
847    }
848
849    #[test]
850    fn parse_rejects_truncated_buffer() {
851        let err = LinkageDescriptor::parse(&[TAG]).unwrap_err();
852        assert!(matches!(err, Error::BufferTooShort { .. }));
853    }
854
855    #[test]
856    fn parse_rejects_truncated_mobile_handover() {
857        let bytes = [
858            TAG, 0x08, // length 8
859            0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x08, // linkage_type = mobile hand-over
860            0x10, // flags byte (origin_type=0, needs initial_service_id)
861            0x00, 0x10, // network_id
862        ];
863        let err = LinkageDescriptor::parse(&bytes).unwrap_err();
864        assert!(
865            matches!(err, Error::InvalidDescriptor { .. }),
866            "expected InvalidDescriptor for truncated mobile hand-over, got {err:?}"
867        );
868    }
869
870    #[test]
871    fn serialize_round_trip_no_linkage_data() {
872        let d = LinkageDescriptor {
873            transport_stream_id: 0x1234,
874            original_network_id: 0x5678,
875            service_id: 0xABCD,
876            linkage_type: 0x02,
877            linkage_data: LinkageData::None,
878            private_data: &[],
879        };
880        let mut buf = vec![0u8; d.serialized_len()];
881        d.serialize_into(&mut buf).unwrap();
882        let re = LinkageDescriptor::parse(&buf).unwrap();
883        assert_eq!(d, re);
884    }
885
886    #[test]
887    fn serialize_round_trip_with_private_data() {
888        let d = LinkageDescriptor {
889            transport_stream_id: 0x0001,
890            original_network_id: 0x0002,
891            service_id: 0x0003,
892            linkage_type: 0x05,
893            linkage_data: LinkageData::None,
894            private_data: &[0xDE, 0xAD, 0xBE, 0xEF],
895        };
896        let mut buf = vec![0u8; d.serialized_len()];
897        d.serialize_into(&mut buf).unwrap();
898        let re = LinkageDescriptor::parse(&buf).unwrap();
899        assert_eq!(d, re);
900    }
901
902    #[test]
903    fn serialize_round_trip_mobile_handover() {
904        let d = LinkageDescriptor {
905            transport_stream_id: 0x0001,
906            original_network_id: 0x0002,
907            service_id: 0x0003,
908            linkage_type: 0x08,
909            linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
910                hand_over_type: 3,
911                origin_type: false,
912                network_id: Some(0x0044),
913                initial_service_id: Some(0x0055),
914            }),
915            private_data: &[0xFF],
916        };
917        let mut buf = vec![0u8; d.serialized_len()];
918        d.serialize_into(&mut buf).unwrap();
919        let re = LinkageDescriptor::parse(&buf).unwrap();
920        assert_eq!(d, re);
921    }
922
923    #[test]
924    fn serialize_round_trip_event_linkage() {
925        let d = LinkageDescriptor {
926            transport_stream_id: 0x0001,
927            original_network_id: 0x0002,
928            service_id: 0x0003,
929            linkage_type: 0x0D,
930            linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
931                target_event_id: 0x1234,
932                target_listed: true,
933                event_simulcast: false,
934            }),
935            private_data: &[],
936        };
937        let mut buf = vec![0u8; d.serialized_len()];
938        d.serialize_into(&mut buf).unwrap();
939        let re = LinkageDescriptor::parse(&buf).unwrap();
940        assert_eq!(d, re);
941    }
942
943    #[test]
944    fn serialize_round_trip_extended_event_linkage() {
945        let d = LinkageDescriptor {
946            transport_stream_id: 0x0001,
947            original_network_id: 0x0002,
948            service_id: 0x0003,
949            linkage_type: 0x0E,
950            linkage_data: LinkageData::ExtendedEventLinkage(ExtendedEventLinkageInfo {
951                entries: vec![ExtendedEventLinkageEntry {
952                    target_event_id: 0xAAAA,
953                    target_listed: true,
954                    event_simulcast: true,
955                    link_type: 1,
956                    target_id_type: 1,
957                    original_network_id_flag: true,
958                    service_id_flag: true,
959                    target_id: TargetId::Dvb {
960                        target_id_type: 1,
961                        target_transport_stream_id: Some(0x1111),
962                        target_original_network_id: Some(0x2222),
963                        target_service_id: Some(0x3333),
964                    },
965                }],
966            }),
967            private_data: &[0xCC],
968        };
969        let mut buf = vec![0u8; d.serialized_len()];
970        d.serialize_into(&mut buf).unwrap();
971        let re = LinkageDescriptor::parse(&buf).unwrap();
972        assert_eq!(d, re);
973    }
974
975    #[test]
976    fn serialize_round_trip_other() {
977        let raw = [0xAA, 0xBB, 0xCC];
978        let d = LinkageDescriptor {
979            transport_stream_id: 0x0001,
980            original_network_id: 0x0002,
981            service_id: 0x0003,
982            linkage_type: 0x0B,
983            linkage_data: LinkageData::Other(&raw),
984            private_data: &[],
985        };
986        let mut buf = vec![0u8; d.serialized_len()];
987        d.serialize_into(&mut buf).unwrap();
988        let re = LinkageDescriptor::parse(&buf).unwrap();
989        assert_eq!(d, re);
990    }
991
992    #[test]
993    fn serialize_reserved_bits_are_set() {
994        let d = LinkageDescriptor {
995            transport_stream_id: 0x0001,
996            original_network_id: 0x0002,
997            service_id: 0x0003,
998            linkage_type: 0x0D,
999            linkage_data: LinkageData::EventLinkage(EventLinkageInfo {
1000                target_event_id: 0x0000,
1001                target_listed: false,
1002                event_simulcast: false,
1003            }),
1004            private_data: &[],
1005        };
1006        let mut buf = vec![0u8; d.serialized_len()];
1007        d.serialize_into(&mut buf).unwrap();
1008        assert_eq!(buf[11] & RESERVED_EVENT_MASK, RESERVED_EVENT_MASK);
1009
1010        let d2 = LinkageDescriptor {
1011            transport_stream_id: 0x0001,
1012            original_network_id: 0x0002,
1013            service_id: 0x0003,
1014            linkage_type: 0x08,
1015            linkage_data: LinkageData::MobileHandOver(MobileHandOverInfo {
1016                hand_over_type: 0,
1017                origin_type: true,
1018                network_id: None,
1019                initial_service_id: None,
1020            }),
1021            private_data: &[],
1022        };
1023        let mut buf2 = vec![0u8; d2.serialized_len()];
1024        d2.serialize_into(&mut buf2).unwrap();
1025        assert_eq!(buf2[9] & RESERVED_HANDOVER_MASK, RESERVED_HANDOVER_MASK);
1026    }
1027
1028    #[test]
1029    fn parse_mobile_handover_type4_no_network_id() {
1030        let bytes = [
1031            TAG, 0x0A, // length 10 = 7 fixed + flags(1) + initial_sid(2)
1032            0x00, 0x01, // ts_id
1033            0x00, 0x02, // onid
1034            0x00, 0x03, // sid
1035            0x08, // linkage_type = mobile hand-over
1036            0x4E, // hand_over_type=4, rfu=111, origin_type=0 (NIT)
1037            0x00, 0x20, // initial_service_id
1038        ];
1039        let d = LinkageDescriptor::parse(&bytes).unwrap();
1040        match &d.linkage_data {
1041            LinkageData::MobileHandOver(m) => {
1042                assert_eq!(m.hand_over_type, 4);
1043                assert!(!m.origin_type);
1044                assert_eq!(m.network_id, None);
1045                assert_eq!(m.initial_service_id, Some(0x0020));
1046            }
1047            other => panic!("expected MobileHandOver, got {other:?}"),
1048        }
1049    }
1050
1051    #[test]
1052    fn parse_mobile_handover_type1_network_id_present() {
1053        let bytes = [
1054            TAG, 0x0C, // length 12 = 7 fixed + 3 handover + 2 priv
1055            0x00, 0x01, // ts_id
1056            0x00, 0x02, // onid
1057            0x00, 0x03, // sid
1058            0x08, // linkage_type = mobile hand-over
1059            0x1F, // hand_over_type=1, rfu=111, origin_type=1 (SDT)
1060            0x00, 0x10, // network_id
1061            0xCA, 0xFE, // private_data
1062        ];
1063        let d = LinkageDescriptor::parse(&bytes).unwrap();
1064        match &d.linkage_data {
1065            LinkageData::MobileHandOver(m) => {
1066                assert_eq!(m.hand_over_type, 1);
1067                assert!(m.origin_type);
1068                assert_eq!(m.network_id, Some(0x0010));
1069                assert_eq!(m.initial_service_id, None);
1070            }
1071            other => panic!("expected MobileHandOver, got {other:?}"),
1072        }
1073        assert_eq!(d.private_data, &[0xCA, 0xFE]);
1074    }
1075}