Skip to main content

dvb_si/descriptors/
nvod_reference.rs

1//! NVOD Reference Descriptor — ETSI EN 300 468 §6.2.28 (tag 0x4B).
2//!
3//! Table 80 (PDF p. 96). Carried in the SDT for an NVOD reference service.
4//! Body is a loop of (transport_stream_id, original_network_id, service_id)
5//! triples, each identifying one of the time-shifted NVOD services.
6
7use super::descriptor_body;
8use crate::error::{Error, Result};
9use alloc::vec::Vec;
10use dvb_common::{Parse, Serialize};
11
12/// Descriptor tag for NVOD_reference_descriptor.
13pub const TAG: u8 = 0x4B;
14const HEADER_LEN: usize = 2;
15const ENTRY_LEN: usize = 6;
16/// Maximum body length expressible in the 8-bit `descriptor_length` field.
17const MAX_BODY_LEN: usize = u8::MAX as usize;
18
19/// One NVOD reference triple.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct NvodReferenceEntry {
23    /// transport_stream_id carrying the referenced service.
24    pub transport_stream_id: u16,
25    /// original_network_id of the referenced service.
26    pub original_network_id: u16,
27    /// service_id of the referenced NVOD time-shifted service.
28    pub service_id: u16,
29}
30
31/// NVOD Reference Descriptor.
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34pub struct NvodReferenceDescriptor {
35    /// Reference triples in wire order.
36    pub entries: Vec<NvodReferenceEntry>,
37}
38
39impl<'a> Parse<'a> for NvodReferenceDescriptor {
40    type Error = crate::error::Error;
41    fn parse(bytes: &'a [u8]) -> Result<Self> {
42        let body = descriptor_body(
43            bytes,
44            TAG,
45            "NvodReferenceDescriptor",
46            "unexpected tag for NVOD_reference_descriptor",
47        )?;
48        if body.len() % ENTRY_LEN != 0 {
49            return Err(Error::InvalidDescriptor {
50                tag: TAG,
51                reason: "descriptor_length must be a multiple of 6",
52            });
53        }
54        let mut entries = Vec::with_capacity(body.len() / ENTRY_LEN);
55        for chunk in body.chunks_exact(ENTRY_LEN) {
56            // chunks_exact(6) guarantees 6 bytes; all unwraps are safe.
57            let (tsid, rest) = chunk.split_first_chunk::<2>().unwrap();
58            let (onid, sid_bytes) = rest.split_first_chunk::<2>().unwrap();
59            let sid = sid_bytes.first_chunk::<2>().unwrap();
60            entries.push(NvodReferenceEntry {
61                transport_stream_id: u16::from_be_bytes(*tsid),
62                original_network_id: u16::from_be_bytes(*onid),
63                service_id: u16::from_be_bytes(*sid),
64            });
65        }
66        Ok(Self { entries })
67    }
68}
69
70impl Serialize for NvodReferenceDescriptor {
71    type Error = crate::error::Error;
72    fn serialized_len(&self) -> usize {
73        HEADER_LEN + ENTRY_LEN * self.entries.len()
74    }
75
76    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
77        let len = self.serialized_len();
78        if buf.len() < len {
79            return Err(Error::OutputBufferTooSmall {
80                need: len,
81                have: buf.len(),
82            });
83        }
84        let body_len = ENTRY_LEN * self.entries.len();
85        // 8-bit descriptor_length field: error rather than silently truncate.
86        if body_len > MAX_BODY_LEN {
87            return Err(Error::InvalidDescriptor {
88                tag: TAG,
89                reason: "NVOD_reference_descriptor body exceeds 255 bytes",
90            });
91        }
92        buf[0] = TAG;
93        buf[1] = body_len as u8;
94        let mut pos = HEADER_LEN;
95        for e in &self.entries {
96            buf[pos..pos + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
97            buf[pos + 2..pos + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
98            buf[pos + 4..pos + 6].copy_from_slice(&e.service_id.to_be_bytes());
99            pos += ENTRY_LEN;
100        }
101        Ok(len)
102    }
103}
104impl<'a> crate::traits::DescriptorDef<'a> for NvodReferenceDescriptor {
105    const TAG: u8 = TAG;
106    const NAME: &'static str = "NVOD_REFERENCE";
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn parse_single_triple() {
115        let bytes = [TAG, 6, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
116        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
117        assert_eq!(d.entries.len(), 1);
118        assert_eq!(d.entries[0].transport_stream_id, 1);
119        assert_eq!(d.entries[0].original_network_id, 2);
120        assert_eq!(d.entries[0].service_id, 3);
121    }
122
123    #[test]
124    fn parse_multiple_triples_preserves_order() {
125        let bytes = [
126            TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06,
127        ];
128        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
129        assert_eq!(d.entries.len(), 2);
130        assert_eq!(d.entries[1].transport_stream_id, 4);
131        assert_eq!(d.entries[1].original_network_id, 5);
132        assert_eq!(d.entries[1].service_id, 6);
133    }
134
135    #[test]
136    fn parse_rejects_wrong_tag() {
137        assert!(matches!(
138            NvodReferenceDescriptor::parse(&[0x4C, 6, 0, 0, 0, 0, 0, 0]).unwrap_err(),
139            Error::InvalidDescriptor { tag: 0x4C, .. }
140        ));
141    }
142
143    #[test]
144    fn parse_rejects_short_buffer() {
145        let bytes = [TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
146        assert!(matches!(
147            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
148            Error::BufferTooShort { .. }
149        ));
150    }
151
152    #[test]
153    fn parse_rejects_length_not_multiple_of_6() {
154        let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x02, 0x00];
155        assert!(matches!(
156            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
157            Error::InvalidDescriptor { tag: TAG, .. }
158        ));
159    }
160
161    #[test]
162    fn empty_descriptor_valid() {
163        let d = NvodReferenceDescriptor::parse(&[TAG, 0]).unwrap();
164        assert!(d.entries.is_empty());
165    }
166
167    #[test]
168    fn serialize_round_trip() {
169        let d = NvodReferenceDescriptor {
170            entries: vec![
171                NvodReferenceEntry {
172                    transport_stream_id: 0x1234,
173                    original_network_id: 0x5678,
174                    service_id: 0x9ABC,
175                },
176                NvodReferenceEntry {
177                    transport_stream_id: 0x0001,
178                    original_network_id: 0x0002,
179                    service_id: 0x0003,
180                },
181            ],
182        };
183        let mut buf = vec![0u8; d.serialized_len()];
184        d.serialize_into(&mut buf).unwrap();
185        assert_eq!(NvodReferenceDescriptor::parse(&buf).unwrap(), d);
186    }
187
188    #[test]
189    fn serialize_rejects_over_range_body() {
190        // 43 triples = 258 body bytes, past the u8 length field.
191        let d = NvodReferenceDescriptor {
192            entries: vec![
193                NvodReferenceEntry {
194                    transport_stream_id: 1,
195                    original_network_id: 2,
196                    service_id: 3,
197                };
198                43
199            ],
200        };
201        let mut buf = vec![0u8; d.serialized_len()];
202        assert!(matches!(
203            d.serialize_into(&mut buf).unwrap_err(),
204            Error::InvalidDescriptor { tag: TAG, .. }
205        ));
206    }
207
208    #[cfg(feature = "serde")]
209    #[test]
210    fn serde_round_trip() {
211        let d = NvodReferenceDescriptor {
212            entries: vec![NvodReferenceEntry {
213                transport_stream_id: 1,
214                original_network_id: 2,
215                service_id: 3,
216            }],
217        };
218        let json = serde_json::to_string(&d).unwrap();
219        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
220        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
221    }
222}