Skip to main content

dvb_si/descriptors/
tva_id.rs

1//! TVA_id Descriptor — ETSI TS 102 323 §11.2.4, Table 114 (tag 0x75).
2//!
3//! Lists one or more TV-Anytime identifiers, each with a running_status that
4//! a receiver uses to drive its recording strategy. Per the TVA PDF
5//! (etsi_ts_102_323_v01.04.01, p. 101, Table 114) each loop entry is 3 bytes:
6//! TVA_id(16) + Reserved(5) + running_status(3). running_status values are
7//! defined in Table 115 (0=reserved, 1=not yet running, 2=starts shortly,
8//! 3=paused, 4=running, 5=cancelled, 6=completed, 7=reserved).
9
10use crate::error::{Error, Result};
11use crate::traits::Descriptor;
12use dvb_common::{Parse, Serialize};
13
14/// Descriptor tag for TVA_id_descriptor.
15pub const TAG: u8 = 0x75;
16const HEADER_LEN: usize = 2;
17const ENTRY_LEN: usize = 3;
18
19/// Largest representable 3-bit running_status.
20const RUNNING_STATUS_MAX: u8 = 0x07;
21
22/// One TVA_id loop entry.
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25pub struct TvaIdEntry {
26    /// 16-bit TVA_id referencing the item of content.
27    pub tva_id: u16,
28    /// 3-bit running_status (Table 115).
29    pub running_status: u8,
30}
31
32/// TVA_id Descriptor.
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35pub struct TvaIdDescriptor {
36    /// Entries in wire order.
37    pub entries: Vec<TvaIdEntry>,
38}
39
40impl<'a> Parse<'a> for TvaIdDescriptor {
41    type Error = crate::error::Error;
42    fn parse(bytes: &'a [u8]) -> Result<Self> {
43        if bytes.len() < HEADER_LEN {
44            return Err(Error::BufferTooShort {
45                need: HEADER_LEN,
46                have: bytes.len(),
47                what: "TvaIdDescriptor header",
48            });
49        }
50        if bytes[0] != TAG {
51            return Err(Error::InvalidDescriptor {
52                tag: bytes[0],
53                reason: "unexpected tag for TVA_id_descriptor",
54            });
55        }
56        let length = bytes[1] as usize;
57        let end = HEADER_LEN + length;
58        if bytes.len() < end {
59            return Err(Error::BufferTooShort {
60                need: end,
61                have: bytes.len(),
62                what: "TvaIdDescriptor body",
63            });
64        }
65        if length % ENTRY_LEN != 0 {
66            return Err(Error::InvalidDescriptor {
67                tag: TAG,
68                reason: "TVA_id_descriptor length must be a multiple of 3",
69            });
70        }
71        let body = &bytes[HEADER_LEN..end];
72        let mut entries = Vec::with_capacity(length / ENTRY_LEN);
73        for chunk in body.chunks_exact(ENTRY_LEN) {
74            let tva_id = u16::from_be_bytes([chunk[0], chunk[1]]);
75            // Reserved(5) ignored on parse.
76            let running_status = chunk[2] & RUNNING_STATUS_MAX;
77            entries.push(TvaIdEntry {
78                tva_id,
79                running_status,
80            });
81        }
82        Ok(Self { entries })
83    }
84}
85
86impl Serialize for TvaIdDescriptor {
87    type Error = crate::error::Error;
88    fn serialized_len(&self) -> usize {
89        HEADER_LEN + self.entries.len() * ENTRY_LEN
90    }
91
92    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
93        for e in &self.entries {
94            if e.running_status > RUNNING_STATUS_MAX {
95                return Err(Error::InvalidDescriptor {
96                    tag: TAG,
97                    reason: "running_status exceeds 3 bits",
98                });
99            }
100        }
101        if self.entries.len() * ENTRY_LEN > u8::MAX as usize {
102            return Err(Error::InvalidDescriptor {
103                tag: TAG,
104                reason: "TVA_id_descriptor body exceeds 255 bytes",
105            });
106        }
107        let len = self.serialized_len();
108        if buf.len() < len {
109            return Err(Error::OutputBufferTooSmall {
110                need: len,
111                have: buf.len(),
112            });
113        }
114        buf[0] = TAG;
115        buf[1] = (self.entries.len() * ENTRY_LEN) as u8;
116        let mut pos = HEADER_LEN;
117        for e in &self.entries {
118            buf[pos..pos + 2].copy_from_slice(&e.tva_id.to_be_bytes());
119            // Reserved(5) emitted as 1s.
120            buf[pos + 2] = 0xF8 | (e.running_status & RUNNING_STATUS_MAX);
121            pos += ENTRY_LEN;
122        }
123        Ok(len)
124    }
125}
126
127impl<'a> Descriptor<'a> for TvaIdDescriptor {
128    const TAG: u8 = TAG;
129    fn descriptor_length(&self) -> u8 {
130        (self.entries.len() * ENTRY_LEN) as u8
131    }
132}
133
134impl<'a> crate::traits::DescriptorDef<'a> for TvaIdDescriptor {
135    const TAG: u8 = TAG;
136    const NAME: &'static str = "TVA_ID";
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn parse_single_entry() {
145        // TVA_id=0x1234, running_status=4 (running), reserved bits set.
146        let bytes = [TAG, 3, 0x12, 0x34, 0xFC];
147        let d = TvaIdDescriptor::parse(&bytes).unwrap();
148        assert_eq!(d.entries.len(), 1);
149        assert_eq!(d.entries[0].tva_id, 0x1234);
150        assert_eq!(d.entries[0].running_status, 4);
151    }
152
153    #[test]
154    fn parse_multiple_entries() {
155        let bytes = [TAG, 6, 0x00, 0x01, 0x01, 0xAB, 0xCD, 0x06];
156        let d = TvaIdDescriptor::parse(&bytes).unwrap();
157        assert_eq!(d.entries.len(), 2);
158        assert_eq!(d.entries[0].tva_id, 0x0001);
159        assert_eq!(d.entries[0].running_status, 1);
160        assert_eq!(d.entries[1].tva_id, 0xABCD);
161        assert_eq!(d.entries[1].running_status, 6);
162    }
163
164    #[test]
165    fn parse_ignores_reserved_bits() {
166        let bytes = [TAG, 3, 0x00, 0x00, 0xFF];
167        let d = TvaIdDescriptor::parse(&bytes).unwrap();
168        assert_eq!(d.entries[0].running_status, 0x07);
169    }
170
171    #[test]
172    fn parse_rejects_wrong_tag() {
173        assert!(matches!(
174            TvaIdDescriptor::parse(&[0x74, 0]).unwrap_err(),
175            Error::InvalidDescriptor { tag: 0x74, .. }
176        ));
177    }
178
179    #[test]
180    fn parse_rejects_length_not_multiple_of_3() {
181        let bytes = [TAG, 2, 0, 0];
182        assert!(matches!(
183            TvaIdDescriptor::parse(&bytes).unwrap_err(),
184            Error::InvalidDescriptor { .. }
185        ));
186    }
187
188    #[test]
189    fn empty_descriptor_valid() {
190        let bytes = [TAG, 0];
191        let d = TvaIdDescriptor::parse(&bytes).unwrap();
192        assert!(d.entries.is_empty());
193    }
194
195    #[test]
196    fn serialize_round_trip() {
197        let d = TvaIdDescriptor {
198            entries: vec![
199                TvaIdEntry {
200                    tva_id: 0x1000,
201                    running_status: 2,
202                },
203                TvaIdEntry {
204                    tva_id: 0xFFFF,
205                    running_status: 0,
206                },
207            ],
208        };
209        let mut buf = vec![0u8; d.serialized_len()];
210        d.serialize_into(&mut buf).unwrap();
211        assert_eq!(TvaIdDescriptor::parse(&buf).unwrap(), d);
212    }
213
214    #[test]
215    fn serialize_rejects_running_status_over_range() {
216        let d = TvaIdDescriptor {
217            entries: vec![TvaIdEntry {
218                tva_id: 0,
219                running_status: 0x08,
220            }],
221        };
222        let mut buf = vec![0u8; d.serialized_len()];
223        assert!(matches!(
224            d.serialize_into(&mut buf).unwrap_err(),
225            Error::InvalidDescriptor { .. }
226        ));
227    }
228
229    #[cfg(feature = "serde")]
230    #[test]
231    fn serde_round_trip() {
232        let d = TvaIdDescriptor {
233            entries: vec![TvaIdEntry {
234                tva_id: 0x4242,
235                running_status: 4,
236            }],
237        };
238        let j = serde_json::to_string(&d).unwrap();
239        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
240        let _v: serde_json::Value = serde_json::from_str(&j).unwrap();
241    }
242}