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
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn parse_single_triple() {
130 let bytes = [TAG, 6, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
131 let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
132 assert_eq!(d.entries.len(), 1);
133 assert_eq!(d.entries[0].transport_stream_id, 1);
134 assert_eq!(d.entries[0].original_network_id, 2);
135 assert_eq!(d.entries[0].service_id, 3);
136 }
137
138 #[test]
139 fn parse_multiple_triples_preserves_order() {
140 let bytes = [
141 TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06,
142 ];
143 let d = NvodReferenceDescriptor::parse(&bytes).unwrap();
144 assert_eq!(d.entries.len(), 2);
145 assert_eq!(d.entries[1].transport_stream_id, 4);
146 assert_eq!(d.entries[1].original_network_id, 5);
147 assert_eq!(d.entries[1].service_id, 6);
148 }
149
150 #[test]
151 fn parse_rejects_wrong_tag() {
152 assert!(matches!(
153 NvodReferenceDescriptor::parse(&[0x4C, 6, 0, 0, 0, 0, 0, 0]).unwrap_err(),
154 Error::InvalidDescriptor { tag: 0x4C, .. }
155 ));
156 }
157
158 #[test]
159 fn parse_rejects_short_buffer() {
160 let bytes = [TAG, 12, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
161 assert!(matches!(
162 NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
163 Error::BufferTooShort { .. }
164 ));
165 }
166
167 #[test]
168 fn parse_rejects_length_not_multiple_of_6() {
169 let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x02, 0x00];
170 assert!(matches!(
171 NvodReferenceDescriptor::parse(&bytes).unwrap_err(),
172 Error::InvalidDescriptor { tag: TAG, .. }
173 ));
174 }
175
176 #[test]
177 fn empty_descriptor_valid() {
178 let d = NvodReferenceDescriptor::parse(&[TAG, 0]).unwrap();
179 assert!(d.entries.is_empty());
180 }
181
182 #[test]
183 fn serialize_round_trip() {
184 let d = NvodReferenceDescriptor {
185 entries: vec![
186 NvodReferenceEntry {
187 transport_stream_id: 0x1234,
188 original_network_id: 0x5678,
189 service_id: 0x9ABC,
190 },
191 NvodReferenceEntry {
192 transport_stream_id: 0x0001,
193 original_network_id: 0x0002,
194 service_id: 0x0003,
195 },
196 ],
197 };
198 let mut buf = vec![0u8; d.serialized_len()];
199 d.serialize_into(&mut buf).unwrap();
200 assert_eq!(NvodReferenceDescriptor::parse(&buf).unwrap(), d);
201 }
202
203 #[test]
204 fn serialize_rejects_over_range_body() {
205 let d = NvodReferenceDescriptor {
207 entries: vec![
208 NvodReferenceEntry {
209 transport_stream_id: 1,
210 original_network_id: 2,
211 service_id: 3,
212 };
213 43
214 ],
215 };
216 let mut buf = vec![0u8; d.serialized_len()];
217 assert!(matches!(
218 d.serialize_into(&mut buf).unwrap_err(),
219 Error::SectionLengthOverflow { .. }
220 ));
221 }
222
223 #[cfg(feature = "serde")]
224 #[test]
225 fn serde_round_trip() {
226 let d = NvodReferenceDescriptor {
227 entries: vec![NvodReferenceEntry {
228 transport_stream_id: 1,
229 original_network_id: 2,
230 service_id: 3,
231 }],
232 };
233 let json = serde_json::to_string(&d).unwrap();
234 let back: NvodReferenceDescriptor = serde_json::from_str(&json).unwrap();
235 assert_eq!(d, back);
236 }
237}