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