1use crate::error::{Error, Result};
22use crate::traits::Descriptor;
23use dvb_common::{Parse, Serialize};
24
25pub const TAG: u8 = 0x6D;
27pub const HEADER_LEN: usize = 2;
29pub const OUTER_FIXED_LEN: usize = 7;
31pub const SUBCELL_LEN: usize = 5;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct CellFrequencyLinkSubcell {
38 pub cell_id_extension: u8,
40 pub transposer_frequency: u32,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct CellFrequencyLinkEntry {
48 pub cell_id: u16,
50 pub frequency: u32,
52 pub subcells: Vec<CellFrequencyLinkSubcell>,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct CellFrequencyLinkDescriptor {
60 pub entries: Vec<CellFrequencyLinkEntry>,
62}
63
64impl<'a> Parse<'a> for CellFrequencyLinkDescriptor {
65 type Error = crate::error::Error;
66 fn parse(bytes: &'a [u8]) -> Result<Self> {
67 if bytes.len() < HEADER_LEN {
68 return Err(Error::BufferTooShort {
69 need: HEADER_LEN,
70 have: bytes.len(),
71 what: "CellFrequencyLinkDescriptor header",
72 });
73 }
74 if bytes[0] != TAG {
75 return Err(Error::InvalidDescriptor {
76 tag: bytes[0],
77 reason: "unexpected tag for cell_frequency_link_descriptor",
78 });
79 }
80 let length = bytes[1] as usize;
81 let end = HEADER_LEN + length;
82 if bytes.len() < end {
83 return Err(Error::BufferTooShort {
84 need: end,
85 have: bytes.len(),
86 what: "CellFrequencyLinkDescriptor body",
87 });
88 }
89 let body = &bytes[HEADER_LEN..end];
90 let mut entries = Vec::new();
91 let mut pos = 0;
92 while pos < body.len() {
93 if pos + OUTER_FIXED_LEN > body.len() {
94 return Err(Error::InvalidDescriptor {
95 tag: TAG,
96 reason: "cell_frequency_link outer entry truncated",
97 });
98 }
99 let cell_id = u16::from_be_bytes([body[pos], body[pos + 1]]);
100 let frequency =
101 u32::from_be_bytes([body[pos + 2], body[pos + 3], body[pos + 4], body[pos + 5]]);
102 let subcell_info_loop_length = body[pos + 6] as usize;
103 pos += OUTER_FIXED_LEN;
104 if subcell_info_loop_length % SUBCELL_LEN != 0 {
105 return Err(Error::InvalidDescriptor {
106 tag: TAG,
107 reason: "subcell_info_loop_length must be a multiple of 5",
108 });
109 }
110 if pos + subcell_info_loop_length > body.len() {
111 return Err(Error::InvalidDescriptor {
112 tag: TAG,
113 reason: "subcell_info_loop_length exceeds descriptor body",
114 });
115 }
116 let subcell_count = subcell_info_loop_length / SUBCELL_LEN;
117 let mut subcells = Vec::with_capacity(subcell_count);
118 for _ in 0..subcell_count {
119 let cell_id_extension = body[pos];
120 let transposer_frequency = u32::from_be_bytes([
121 body[pos + 1],
122 body[pos + 2],
123 body[pos + 3],
124 body[pos + 4],
125 ]);
126 subcells.push(CellFrequencyLinkSubcell {
127 cell_id_extension,
128 transposer_frequency,
129 });
130 pos += SUBCELL_LEN;
131 }
132 entries.push(CellFrequencyLinkEntry {
133 cell_id,
134 frequency,
135 subcells,
136 });
137 }
138 Ok(Self { entries })
139 }
140}
141
142impl CellFrequencyLinkDescriptor {
143 fn body_len(&self) -> usize {
144 self.entries
145 .iter()
146 .map(|e| OUTER_FIXED_LEN + e.subcells.len() * SUBCELL_LEN)
147 .sum()
148 }
149}
150
151impl Serialize for CellFrequencyLinkDescriptor {
152 type Error = crate::error::Error;
153 fn serialized_len(&self) -> usize {
154 HEADER_LEN + self.body_len()
155 }
156
157 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
158 let body_len = self.body_len();
159 if body_len > u8::MAX as usize {
160 return Err(Error::InvalidDescriptor {
161 tag: TAG,
162 reason: "cell_frequency_link_descriptor body exceeds 255 bytes",
163 });
164 }
165 for e in &self.entries {
166 if e.subcells.len() * SUBCELL_LEN > u8::MAX as usize {
167 return Err(Error::InvalidDescriptor {
168 tag: TAG,
169 reason: "subcell_info_loop_length exceeds 255 bytes",
170 });
171 }
172 }
173 let len = self.serialized_len();
174 if buf.len() < len {
175 return Err(Error::OutputBufferTooSmall {
176 need: len,
177 have: buf.len(),
178 });
179 }
180 buf[0] = TAG;
181 buf[1] = body_len as u8;
182 let mut pos = HEADER_LEN;
183 for e in &self.entries {
184 buf[pos..pos + 2].copy_from_slice(&e.cell_id.to_be_bytes());
185 buf[pos + 2..pos + 6].copy_from_slice(&e.frequency.to_be_bytes());
186 buf[pos + 6] = (e.subcells.len() * SUBCELL_LEN) as u8;
187 pos += OUTER_FIXED_LEN;
188 for sc in &e.subcells {
189 buf[pos] = sc.cell_id_extension;
190 buf[pos + 1..pos + 5].copy_from_slice(&sc.transposer_frequency.to_be_bytes());
191 pos += SUBCELL_LEN;
192 }
193 }
194 Ok(len)
195 }
196}
197
198impl<'a> Descriptor<'a> for CellFrequencyLinkDescriptor {
199 const TAG: u8 = TAG;
200 fn descriptor_length(&self) -> u8 {
201 self.body_len() as u8
202 }
203}
204
205impl<'a> crate::traits::DescriptorDef<'a> for CellFrequencyLinkDescriptor {
206 const TAG: u8 = TAG;
207 const NAME: &'static str = "CELL_FREQUENCY_LINK";
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn parse_entry_with_subcells() {
216 let bytes = [
217 TAG, 17, 0x12, 0x34, 0x00, 0x11, 0x22, 0x33, 10, 0x01, 0x0A, 0xAB, 0xBC, 0xCD, 0x02, 0x0D, 0xDE, 0xEF, 0xF0,
222 ];
223 let d = CellFrequencyLinkDescriptor::parse(&bytes).unwrap();
224 assert_eq!(d.entries.len(), 1);
225 assert_eq!(d.entries[0].cell_id, 0x1234);
226 assert_eq!(d.entries[0].frequency, 0x0011_2233);
227 assert_eq!(d.entries[0].subcells.len(), 2);
228 assert_eq!(d.entries[0].subcells[0].cell_id_extension, 0x01);
229 assert_eq!(d.entries[0].subcells[0].transposer_frequency, 0x0AAB_BCCD);
230 assert_eq!(d.entries[0].subcells[1].cell_id_extension, 0x02);
231 }
232
233 #[test]
234 fn parse_entry_no_subcells() {
235 let bytes = [TAG, 7, 0x00, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00];
236 let d = CellFrequencyLinkDescriptor::parse(&bytes).unwrap();
237 assert_eq!(d.entries.len(), 1);
238 assert_eq!(d.entries[0].cell_id, 0x0005);
239 assert!(d.entries[0].subcells.is_empty());
240 }
241
242 #[test]
243 fn empty_body_is_valid() {
244 let d = CellFrequencyLinkDescriptor::parse(&[TAG, 0]).unwrap();
245 assert!(d.entries.is_empty());
246 }
247
248 #[test]
249 fn parse_rejects_wrong_tag() {
250 assert!(matches!(
251 CellFrequencyLinkDescriptor::parse(&[0x6E, 0]).unwrap_err(),
252 Error::InvalidDescriptor { tag: 0x6E, .. }
253 ));
254 }
255
256 #[test]
257 fn parse_rejects_truncated_outer() {
258 let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x00, 0x10];
260 assert!(matches!(
261 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
262 Error::InvalidDescriptor { tag: TAG, .. }
263 ));
264 }
265
266 #[test]
267 fn parse_rejects_subcell_loop_overrun() {
268 let bytes = [TAG, 7, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 10];
270 assert!(matches!(
271 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
272 Error::InvalidDescriptor { tag: TAG, .. }
273 ));
274 }
275
276 #[test]
277 fn parse_rejects_buffer_shorter_than_length() {
278 let bytes = [TAG, 7, 0x00, 0x01, 0x00];
279 assert!(matches!(
280 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
281 Error::BufferTooShort { .. }
282 ));
283 }
284
285 #[test]
286 fn serialize_round_trip() {
287 let d = CellFrequencyLinkDescriptor {
288 entries: vec![
289 CellFrequencyLinkEntry {
290 cell_id: 0x1234,
291 frequency: 0x0011_2233,
292 subcells: vec![
293 CellFrequencyLinkSubcell {
294 cell_id_extension: 0x01,
295 transposer_frequency: 0x0AAB_BCCD,
296 },
297 CellFrequencyLinkSubcell {
298 cell_id_extension: 0x02,
299 transposer_frequency: 0x0DDE_EFF0,
300 },
301 ],
302 },
303 CellFrequencyLinkEntry {
304 cell_id: 0x9999,
305 frequency: 0x4455_6677,
306 subcells: vec![],
307 },
308 ],
309 };
310 let mut buf = vec![0u8; d.serialized_len()];
311 d.serialize_into(&mut buf).unwrap();
312 assert_eq!(CellFrequencyLinkDescriptor::parse(&buf).unwrap(), d);
313 }
314
315 #[test]
316 fn serialize_rejects_too_small_buffer() {
317 let d = CellFrequencyLinkDescriptor {
318 entries: vec![CellFrequencyLinkEntry {
319 cell_id: 0,
320 frequency: 0,
321 subcells: vec![],
322 }],
323 };
324 let mut buf = vec![0u8; 3];
325 assert!(matches!(
326 d.serialize_into(&mut buf).unwrap_err(),
327 Error::OutputBufferTooSmall { .. }
328 ));
329 }
330
331 #[test]
332 fn serialize_rejects_over_range_body() {
333 let d = CellFrequencyLinkDescriptor {
335 entries: (0..37)
336 .map(|_| CellFrequencyLinkEntry {
337 cell_id: 0,
338 frequency: 0,
339 subcells: vec![],
340 })
341 .collect(),
342 };
343 let mut buf = vec![0u8; d.serialized_len()];
344 assert!(matches!(
345 d.serialize_into(&mut buf).unwrap_err(),
346 Error::InvalidDescriptor { tag: TAG, .. }
347 ));
348 }
349
350 #[cfg(feature = "serde")]
351 #[test]
352 fn serde_round_trip() {
353 let d = CellFrequencyLinkDescriptor {
354 entries: vec![CellFrequencyLinkEntry {
355 cell_id: 0x1234,
356 frequency: 0x0011_2233,
357 subcells: vec![CellFrequencyLinkSubcell {
358 cell_id_extension: 0x01,
359 transposer_frequency: 0x0AAB_BCCD,
360 }],
361 }],
362 };
363 let json = serde_json::to_string(&d).unwrap();
364 let back: CellFrequencyLinkDescriptor = serde_json::from_str(&json).unwrap();
365 assert_eq!(back, d);
366 }
367}