1use crate::error::{Error, Result};
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11pub const TAG: u8 = 0x4B;
13const HEADER_LEN: usize = 2;
14const ENTRY_LEN: usize = 6;
15const MAX_BODY_LEN: usize = u8::MAX as usize;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct NvodReferenceEntry {
22 pub transport_stream_id: u16,
24 pub original_network_id: u16,
26 pub service_id: u16,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct NvodReferenceDescriptor {
34 pub entries: Vec<NvodReferenceEntry>,
36}
37
38impl<'a> Parse<'a> for NvodReferenceDescriptor {
39 type Error = crate::error::Error;
40 fn parse(bytes: &'a [u8]) -> Result<Self> {
41 if bytes.len() < HEADER_LEN {
42 return Err(Error::BufferTooShort {
43 need: HEADER_LEN,
44 have: bytes.len(),
45 what: "NvodReferenceDescriptor header",
46 });
47 }
48 if bytes[0] != TAG {
49 return Err(Error::InvalidDescriptor {
50 tag: bytes[0],
51 reason: "unexpected tag for NVOD_reference_descriptor",
52 });
53 }
54 let length = bytes[1] as usize;
55 if length % ENTRY_LEN != 0 {
56 return Err(Error::InvalidDescriptor {
57 tag: TAG,
58 reason: "descriptor_length must be a multiple of 6",
59 });
60 }
61 let end = HEADER_LEN + length;
62 if bytes.len() < end {
63 return Err(Error::BufferTooShort {
64 need: end,
65 have: bytes.len(),
66 what: "NvodReferenceDescriptor body",
67 });
68 }
69 let body = &bytes[HEADER_LEN..end];
70 let mut entries = Vec::with_capacity(length / ENTRY_LEN);
71 for chunk in body.chunks_exact(ENTRY_LEN) {
72 entries.push(NvodReferenceEntry {
73 transport_stream_id: u16::from_be_bytes([chunk[0], chunk[1]]),
74 original_network_id: u16::from_be_bytes([chunk[2], chunk[3]]),
75 service_id: u16::from_be_bytes([chunk[4], chunk[5]]),
76 });
77 }
78 Ok(Self { entries })
79 }
80}
81
82impl Serialize for NvodReferenceDescriptor {
83 type Error = crate::error::Error;
84 fn serialized_len(&self) -> usize {
85 HEADER_LEN + ENTRY_LEN * self.entries.len()
86 }
87
88 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
89 let len = self.serialized_len();
90 if buf.len() < len {
91 return Err(Error::OutputBufferTooSmall {
92 need: len,
93 have: buf.len(),
94 });
95 }
96 let body_len = ENTRY_LEN * self.entries.len();
97 if body_len > MAX_BODY_LEN {
99 return Err(Error::SectionLengthOverflow {
100 declared: body_len,
101 available: MAX_BODY_LEN,
102 });
103 }
104 buf[0] = TAG;
105 buf[1] = body_len as u8;
106 let mut pos = HEADER_LEN;
107 for e in &self.entries {
108 buf[pos..pos + 2].copy_from_slice(&e.transport_stream_id.to_be_bytes());
109 buf[pos + 2..pos + 4].copy_from_slice(&e.original_network_id.to_be_bytes());
110 buf[pos + 4..pos + 6].copy_from_slice(&e.service_id.to_be_bytes());
111 pos += ENTRY_LEN;
112 }
113 Ok(len)
114 }
115}
116
117impl<'a> Descriptor<'a> for NvodReferenceDescriptor {
118 const TAG: u8 = TAG;
119 fn descriptor_length(&self) -> u8 {
120 (ENTRY_LEN * self.entries.len()) as u8
121 }
122}
123
124impl<'a> crate::traits::DescriptorDef<'a> for NvodReferenceDescriptor {
125 const TAG: u8 = TAG;
126 const NAME: &'static str = "NVOD_REFERENCE";
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn parse_single_triple() {
135 let bytes = [TAG, 6, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
136 let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
137 assert_eq!(d.entries.len(), 1);
138 assert_eq!(d.entries[0].transport_stream_id, 1);
139 assert_eq!(d.entries[0].original_network_id, 2);
140 assert_eq!(d.entries[0].service_id, 3);
141 }
142
143 #[test]
144 fn parse_multiple_triples_preserves_order() {
145 let bytes = [
146 TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06,
147 ];
148 let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
149 assert_eq!(d.entries.len(), 2);
150 assert_eq!(d.entries[1].transport_stream_id, 4);
151 assert_eq!(d.entries[1].original_network_id, 5);
152 assert_eq!(d.entries[1].service_id, 6);
153 }
154
155 #[test]
156 fn parse_rejects_wrong_tag() {
157 assert!(matches!(
158 NvodReferenceDescriptor::parse(&[0x4C, 6, 0, 0, 0, 0, 0, 0]).unwrap_err(),
159 Error::InvalidDescriptor { tag: 0x4C, .. }
160 ));
161 }
162
163 #[test]
164 fn parse_rejects_short_buffer() {
165 let bytes = [TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
166 assert!(matches!(
167 NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
168 Error::BufferTooShort { .. }
169 ));
170 }
171
172 #[test]
173 fn parse_rejects_length_not_multiple_of_6() {
174 let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x02, 0x00];
175 assert!(matches!(
176 NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
177 Error::InvalidDescriptor { tag: TAG, .. }
178 ));
179 }
180
181 #[test]
182 fn empty_descriptor_valid() {
183 let d = NvodReferenceDescriptor::parse(&[TAG, 0]).unwrap();
184 assert!(d.entries.is_empty());
185 }
186
187 #[test]
188 fn serialize_round_trip() {
189 let d = NvodReferenceDescriptor {
190 entries: vec![
191 NvodReferenceEntry {
192 transport_stream_id: 0x1234,
193 original_network_id: 0x5678,
194 service_id: 0x9ABC,
195 },
196 NvodReferenceEntry {
197 transport_stream_id: 0x0001,
198 original_network_id: 0x0002,
199 service_id: 0x0003,
200 },
201 ],
202 };
203 let mut buf = vec![0u8; d.serialized_len()];
204 d.serialize_into(&mut buf).unwrap();
205 assert_eq!(NvodReferenceDescriptor::parse(&buf).unwrap(), d);
206 }
207
208 #[test]
209 fn serialize_rejects_over_range_body() {
210 let d = NvodReferenceDescriptor {
212 entries: vec![
213 NvodReferenceEntry {
214 transport_stream_id: 1,
215 original_network_id: 2,
216 service_id: 3,
217 };
218 43
219 ],
220 };
221 let mut buf = vec![0u8; d.serialized_len()];
222 assert!(matches!(
223 d.serialize_into(&mut buf).unwrap_err(),
224 Error::SectionLengthOverflow { .. }
225 ));
226 }
227
228 #[cfg(feature = "serde")]
229 #[test]
230 fn serde_round_trip() {
231 let d = NvodReferenceDescriptor {
232 entries: vec![NvodReferenceEntry {
233 transport_stream_id: 1,
234 original_network_id: 2,
235 service_id: 3,
236 }],
237 };
238 let json = serde_json::to_string(&d).unwrap();
239 let back: NvodReferenceDescriptor = serde_json::from_str(&json).unwrap();
240 assert_eq!(d, back);
241 }
242}