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
100 .get(pos..)
101 .and_then(|s| s.split_first_chunk::<OUTER_FIXED_LEN>())
102 .ok_or(Error::InvalidDescriptor {
103 tag: TAG,
104 reason: "cell_frequency_link outer entry truncated",
105 })?;
106 let cell_id = u16::from_be_bytes([outer[0], outer[1]]);
107 let frequency = u32::from_be_bytes([outer[2], outer[3], outer[4], outer[5]]);
108 let subcell_info_loop_length = outer[6] as usize;
109 pos += OUTER_FIXED_LEN;
110 if subcell_info_loop_length % SUBCELL_LEN != 0 {
111 return Err(Error::InvalidDescriptor {
112 tag: TAG,
113 reason: "subcell_info_loop_length must be a multiple of 5",
114 });
115 }
116 if pos + subcell_info_loop_length > body.len() {
117 return Err(Error::InvalidDescriptor {
118 tag: TAG,
119 reason: "subcell_info_loop_length exceeds descriptor body",
120 });
121 }
122 let subcell_count = subcell_info_loop_length / SUBCELL_LEN;
123 let mut subcells = Vec::with_capacity(subcell_count);
124 for _ in 0..subcell_count {
125 let (sub, _) = body
126 .get(pos..)
127 .and_then(|s| s.split_first_chunk::<SUBCELL_LEN>())
128 .ok_or(Error::InvalidDescriptor {
129 tag: TAG,
130 reason: "subcell_info_loop_length exceeds descriptor body",
131 })?;
132 let cell_id_extension = sub[0];
133 let transposer_frequency = u32::from_be_bytes([sub[1], sub[2], sub[3], sub[4]]);
134 subcells.push(CellFrequencyLinkSubcell {
135 cell_id_extension,
136 transposer_frequency,
137 });
138 pos += SUBCELL_LEN;
139 }
140 entries.push(CellFrequencyLinkEntry {
141 cell_id,
142 frequency,
143 subcells,
144 });
145 }
146 Ok(Self { entries })
147 }
148}
149
150impl CellFrequencyLinkDescriptor {
151 fn body_len(&self) -> usize {
152 self.entries
153 .iter()
154 .map(|e| OUTER_FIXED_LEN + e.subcells.len() * SUBCELL_LEN)
155 .sum()
156 }
157}
158
159impl Serialize for CellFrequencyLinkDescriptor {
160 type Error = crate::error::Error;
161 fn serialized_len(&self) -> usize {
162 HEADER_LEN + self.body_len()
163 }
164
165 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
166 let body_len = self.body_len();
167 if body_len > u8::MAX as usize {
168 return Err(Error::InvalidDescriptor {
169 tag: TAG,
170 reason: "cell_frequency_link_descriptor body exceeds 255 bytes",
171 });
172 }
173 for e in &self.entries {
174 if e.subcells.len() * SUBCELL_LEN > u8::MAX as usize {
175 return Err(Error::InvalidDescriptor {
176 tag: TAG,
177 reason: "subcell_info_loop_length exceeds 255 bytes",
178 });
179 }
180 }
181 let len = self.serialized_len();
182 if buf.len() < len {
183 return Err(Error::OutputBufferTooSmall {
184 need: len,
185 have: buf.len(),
186 });
187 }
188 buf[0] = TAG;
189 buf[1] = body_len as u8;
190 let mut pos = HEADER_LEN;
191 for e in &self.entries {
192 buf[pos..pos + 2].copy_from_slice(&e.cell_id.to_be_bytes());
193 buf[pos + 2..pos + 6].copy_from_slice(&e.frequency.to_be_bytes());
194 buf[pos + 6] = (e.subcells.len() * SUBCELL_LEN) as u8;
195 pos += OUTER_FIXED_LEN;
196 for sc in &e.subcells {
197 buf[pos] = sc.cell_id_extension;
198 buf[pos + 1..pos + 5].copy_from_slice(&sc.transposer_frequency.to_be_bytes());
199 pos += SUBCELL_LEN;
200 }
201 }
202 Ok(len)
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 frequency_hz_computes_correctly() {
216 let entry = CellFrequencyLinkEntry {
217 cell_id: 0,
218 frequency: 100_000,
219 subcells: vec![],
220 };
221 assert_eq!(entry.frequency_hz(), 1_000_000);
222 }
223
224 #[test]
225 fn transposer_frequency_hz_computes_correctly() {
226 let sc = CellFrequencyLinkSubcell {
227 cell_id_extension: 0,
228 transposer_frequency: 500_000,
229 };
230 assert_eq!(sc.transposer_frequency_hz(), 5_000_000);
231 }
232
233 #[test]
234 fn parse_entry_with_subcells() {
235 let bytes = [
236 TAG, 17, 0x12, 0x34, 0x00, 0x11, 0x22, 0x33, 10, 0x01, 0x0A, 0xAB, 0xBC, 0xCD, 0x02, 0x0D, 0xDE, 0xEF, 0xF0,
241 ];
242 let d = CellFrequencyLinkDescriptor::parse(&bytes).unwrap();
243 assert_eq!(d.entries.len(), 1);
244 assert_eq!(d.entries[0].cell_id, 0x1234);
245 assert_eq!(d.entries[0].frequency, 0x0011_2233);
246 assert_eq!(d.entries[0].subcells.len(), 2);
247 assert_eq!(d.entries[0].subcells[0].cell_id_extension, 0x01);
248 assert_eq!(d.entries[0].subcells[0].transposer_frequency, 0x0AAB_BCCD);
249 assert_eq!(d.entries[0].subcells[1].cell_id_extension, 0x02);
250 }
251
252 #[test]
253 fn parse_entry_no_subcells() {
254 let bytes = [TAG, 7, 0x00, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00];
255 let d = CellFrequencyLinkDescriptor::parse(&bytes).unwrap();
256 assert_eq!(d.entries.len(), 1);
257 assert_eq!(d.entries[0].cell_id, 0x0005);
258 assert!(d.entries[0].subcells.is_empty());
259 }
260
261 #[test]
262 fn empty_body_is_valid() {
263 let d = CellFrequencyLinkDescriptor::parse(&[TAG, 0]).unwrap();
264 assert!(d.entries.is_empty());
265 }
266
267 #[test]
268 fn parse_rejects_wrong_tag() {
269 assert!(matches!(
270 CellFrequencyLinkDescriptor::parse(&[0x6E, 0]).unwrap_err(),
271 Error::InvalidDescriptor { tag: 0x6E, .. }
272 ));
273 }
274
275 #[test]
276 fn parse_rejects_truncated_outer() {
277 let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x00, 0x10];
279 assert!(matches!(
280 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
281 Error::InvalidDescriptor { tag: TAG, .. }
282 ));
283 }
284
285 #[test]
286 fn parse_rejects_subcell_loop_overrun() {
287 let bytes = [TAG, 7, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 10];
289 assert!(matches!(
290 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
291 Error::InvalidDescriptor { tag: TAG, .. }
292 ));
293 }
294
295 #[test]
296 fn parse_rejects_buffer_shorter_than_length() {
297 let bytes = [TAG, 7, 0x00, 0x01, 0x00];
298 assert!(matches!(
299 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
300 Error::BufferTooShort { .. }
301 ));
302 }
303
304 #[test]
305 fn serialize_round_trip() {
306 let d = CellFrequencyLinkDescriptor {
307 entries: vec![
308 CellFrequencyLinkEntry {
309 cell_id: 0x1234,
310 frequency: 0x0011_2233,
311 subcells: vec![
312 CellFrequencyLinkSubcell {
313 cell_id_extension: 0x01,
314 transposer_frequency: 0x0AAB_BCCD,
315 },
316 CellFrequencyLinkSubcell {
317 cell_id_extension: 0x02,
318 transposer_frequency: 0x0DDE_EFF0,
319 },
320 ],
321 },
322 CellFrequencyLinkEntry {
323 cell_id: 0x9999,
324 frequency: 0x4455_6677,
325 subcells: vec![],
326 },
327 ],
328 };
329 let mut buf = vec![0u8; d.serialized_len()];
330 d.serialize_into(&mut buf).unwrap();
331 assert_eq!(CellFrequencyLinkDescriptor::parse(&buf).unwrap(), d);
332 }
333
334 #[test]
335 fn serialize_rejects_too_small_buffer() {
336 let d = CellFrequencyLinkDescriptor {
337 entries: vec![CellFrequencyLinkEntry {
338 cell_id: 0,
339 frequency: 0,
340 subcells: vec![],
341 }],
342 };
343 let mut buf = vec![0u8; 3];
344 assert!(matches!(
345 d.serialize_into(&mut buf).unwrap_err(),
346 Error::OutputBufferTooSmall { .. }
347 ));
348 }
349
350 #[test]
351 fn serialize_rejects_over_range_body() {
352 let d = CellFrequencyLinkDescriptor {
354 entries: (0..37)
355 .map(|_| CellFrequencyLinkEntry {
356 cell_id: 0,
357 frequency: 0,
358 subcells: vec![],
359 })
360 .collect(),
361 };
362 let mut buf = vec![0u8; d.serialized_len()];
363 assert!(matches!(
364 d.serialize_into(&mut buf).unwrap_err(),
365 Error::InvalidDescriptor { tag: TAG, .. }
366 ));
367 }
368
369 #[cfg(feature = "serde")]
370 #[test]
371 fn serde_round_trip() {
372 let d = CellFrequencyLinkDescriptor {
373 entries: vec![CellFrequencyLinkEntry {
374 cell_id: 0x1234,
375 frequency: 0x0011_2233,
376 subcells: vec![CellFrequencyLinkSubcell {
377 cell_id_extension: 0x01,
378 transposer_frequency: 0x0AAB_BCCD,
379 }],
380 }],
381 };
382 let json = serde_json::to_string(&d).unwrap();
383 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
385 }
386}