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