1use super::descriptor_body;
8use crate::error::{Error, Result};
9use alloc::vec::Vec;
10use dvb_common::{Parse, Serialize};
11
12pub const TAG: u8 = 0x4B;
14const HEADER_LEN: usize = 2;
15const ENTRY_LEN: usize = 6;
16const MAX_BODY_LEN: usize = u8::MAX as usize;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct NvodReferenceEntry {
23 pub transport_stream_id: u16,
25 pub original_network_id: u16,
27 pub service_id: u16,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34pub struct NvodReferenceDescriptor {
35 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 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 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 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
217 }
218}