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            entries.push(NvodReferenceEntry {
57                transport_stream_id: u16::from_be_bytes([chunk[0], chunk[1]]),
58                original_network_id: u16::from_be_bytes([chunk[2], chunk[3]]),
59                service_id: u16::from_be_bytes([chunk[4], chunk[5]]),
60            });
61        }
62        Ok(Self { entries })
63    }
64}
65
66impl Serialize for NvodReferenceDescriptor {
67    type Error = crate::error::Error;
68    fn serialized_len(&self) -> usize {
69        HEADER_LEN + ENTRY_LEN * self.entries.len()
70    }
71
72    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
73        let len = self.serialized_len();
74        if buf.len() < len {
75            return Err(Error::OutputBufferTooSmall {
76                need: len,
77                have: buf.len(),
78            });
79        }
80        let body_len = ENTRY_LEN * self.entries.len();
81        // 8-bit descriptor_length field: error rather than silently truncate.
82        if body_len > MAX_BODY_LEN {
83            return Err(Error::InvalidDescriptor {
84                tag: TAG,
85                reason: "NVOD_reference_descriptor body exceeds 255 bytes",
86            });
87        }
88        buf[0] = TAG;
89        buf[1] = body_len as u8;
90        let mut pos = HEADER_LEN;
91        for e in &self.entries {
92            buf[pos..pos + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
93            buf[pos + 2..pos + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
94            buf[pos + 4..pos + 6].copy_from_slice(&e.service_id.to_be_bytes());
95            pos += ENTRY_LEN;
96        }
97        Ok(len)
98    }
99}
100impl<'a> crate::traits::DescriptorDef<'a> for NvodReferenceDescriptor {
101    const TAG: u8 = TAG;
102    const NAME: &'static str = "NVOD_REFERENCE";
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn parse_single_triple() {
111        let bytes = [TAG, 6, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
112        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
113        assert_eq!(d.entries.len(), 1);
114        assert_eq!(d.entries[0].transport_stream_id, 1);
115        assert_eq!(d.entries[0].original_network_id, 2);
116        assert_eq!(d.entries[0].service_id, 3);
117    }
118
119    #[test]
120    fn parse_multiple_triples_preserves_order() {
121        let bytes = [
122            TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06,
123        ];
124        let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
125        assert_eq!(d.entries.len(), 2);
126        assert_eq!(d.entries[1].transport_stream_id, 4);
127        assert_eq!(d.entries[1].original_network_id, 5);
128        assert_eq!(d.entries[1].service_id, 6);
129    }
130
131    #[test]
132    fn parse_rejects_wrong_tag() {
133        assert!(matches!(
134            NvodReferenceDescriptor::parse(&[0x4C, 6, 0, 0, 0, 0, 0, 0]).unwrap_err(),
135            Error::InvalidDescriptor { tag: 0x4C, .. }
136        ));
137    }
138
139    #[test]
140    fn parse_rejects_short_buffer() {
141        let bytes = [TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
142        assert!(matches!(
143            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
144            Error::BufferTooShort { .. }
145        ));
146    }
147
148    #[test]
149    fn parse_rejects_length_not_multiple_of_6() {
150        let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x02, 0x00];
151        assert!(matches!(
152            NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
153            Error::InvalidDescriptor { tag: TAG, .. }
154        ));
155    }
156
157    #[test]
158    fn empty_descriptor_valid() {
159        let d = NvodReferenceDescriptor::parse(&[TAG, 0]).unwrap();
160        assert!(d.entries.is_empty());
161    }
162
163    #[test]
164    fn serialize_round_trip() {
165        let d = NvodReferenceDescriptor {
166            entries: vec![
167                NvodReferenceEntry {
168                    transport_stream_id: 0x1234,
169                    original_network_id: 0x5678,
170                    service_id: 0x9ABC,
171                },
172                NvodReferenceEntry {
173                    transport_stream_id: 0x0001,
174                    original_network_id: 0x0002,
175                    service_id: 0x0003,
176                },
177            ],
178        };
179        let mut buf = vec![0u8; d.serialized_len()];
180        d.serialize_into(&mut buf).unwrap();
181        assert_eq!(NvodReferenceDescriptor::parse(&buf).unwrap(), d);
182    }
183
184    #[test]
185    fn serialize_rejects_over_range_body() {
186        // 43 triples = 258 body bytes, past the u8 length field.
187        let d = NvodReferenceDescriptor {
188            entries: vec![
189                NvodReferenceEntry {
190                    transport_stream_id: 1,
191                    original_network_id: 2,
192                    service_id: 3,
193                };
194                43
195            ],
196        };
197        let mut buf = vec![0u8; d.serialized_len()];
198        assert!(matches!(
199            d.serialize_into(&mut buf).unwrap_err(),
200            Error::InvalidDescriptor { tag: TAG, .. }
201        ));
202    }
203
204    #[cfg(feature = "serde")]
205    #[test]
206    fn serde_round_trip() {
207        let d = NvodReferenceDescriptor {
208            entries: vec![NvodReferenceEntry {
209                transport_stream_id: 1,
210                original_network_id: 2,
211                service_id: 3,
212            }],
213        };
214        let json = serde_json::to_string(&d).unwrap();
215        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
216        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
217    }
218}