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 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}
197impl<'a> crate::traits::DescriptorDef<'a> for CellFrequencyLinkDescriptor {
198 const TAG: u8 = TAG;
199 const NAME: &'static str = "CELL_FREQUENCY_LINK";
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn frequency_hz_computes_correctly() {
208 let entry = CellFrequencyLinkEntry {
209 cell_id: 0,
210 frequency: 100_000,
211 subcells: vec![],
212 };
213 assert_eq!(entry.frequency_hz(), 1_000_000);
214 }
215
216 #[test]
217 fn transposer_frequency_hz_computes_correctly() {
218 let sc = CellFrequencyLinkSubcell {
219 cell_id_extension: 0,
220 transposer_frequency: 500_000,
221 };
222 assert_eq!(sc.transposer_frequency_hz(), 5_000_000);
223 }
224
225 #[test]
226 fn parse_entry_with_subcells() {
227 let bytes = [
228 TAG, 17, 0x12, 0x34, 0x00, 0x11, 0x22, 0x33, 10, 0x01, 0x0A, 0xAB, 0xBC, 0xCD, 0x02, 0x0D, 0xDE, 0xEF, 0xF0,
233 ];
234 let d = CellFrequencyLinkDescriptor::parse(&bytes).unwrap();
235 assert_eq!(d.entries.len(), 1);
236 assert_eq!(d.entries[0].cell_id, 0x1234);
237 assert_eq!(d.entries[0].frequency, 0x0011_2233);
238 assert_eq!(d.entries[0].subcells.len(), 2);
239 assert_eq!(d.entries[0].subcells[0].cell_id_extension, 0x01);
240 assert_eq!(d.entries[0].subcells[0].transposer_frequency, 0x0AAB_BCCD);
241 assert_eq!(d.entries[0].subcells[1].cell_id_extension, 0x02);
242 }
243
244 #[test]
245 fn parse_entry_no_subcells() {
246 let bytes = [TAG, 7, 0x00, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00];
247 let d = CellFrequencyLinkDescriptor::parse(&bytes).unwrap();
248 assert_eq!(d.entries.len(), 1);
249 assert_eq!(d.entries[0].cell_id, 0x0005);
250 assert!(d.entries[0].subcells.is_empty());
251 }
252
253 #[test]
254 fn empty_body_is_valid() {
255 let d = CellFrequencyLinkDescriptor::parse(&[TAG, 0]).unwrap();
256 assert!(d.entries.is_empty());
257 }
258
259 #[test]
260 fn parse_rejects_wrong_tag() {
261 assert!(matches!(
262 CellFrequencyLinkDescriptor::parse(&[0x6E, 0]).unwrap_err(),
263 Error::InvalidDescriptor { tag: 0x6E, .. }
264 ));
265 }
266
267 #[test]
268 fn parse_rejects_truncated_outer() {
269 let bytes = [TAG, 5, 0x00, 0x01, 0x00, 0x00, 0x10];
271 assert!(matches!(
272 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
273 Error::InvalidDescriptor { tag: TAG, .. }
274 ));
275 }
276
277 #[test]
278 fn parse_rejects_subcell_loop_overrun() {
279 let bytes = [TAG, 7, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 10];
281 assert!(matches!(
282 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
283 Error::InvalidDescriptor { tag: TAG, .. }
284 ));
285 }
286
287 #[test]
288 fn parse_rejects_buffer_shorter_than_length() {
289 let bytes = [TAG, 7, 0x00, 0x01, 0x00];
290 assert!(matches!(
291 CellFrequencyLinkDescriptor::parse(&bytes).unwrap_err(),
292 Error::BufferTooShort { .. }
293 ));
294 }
295
296 #[test]
297 fn serialize_round_trip() {
298 let d = CellFrequencyLinkDescriptor {
299 entries: vec![
300 CellFrequencyLinkEntry {
301 cell_id: 0x1234,
302 frequency: 0x0011_2233,
303 subcells: vec![
304 CellFrequencyLinkSubcell {
305 cell_id_extension: 0x01,
306 transposer_frequency: 0x0AAB_BCCD,
307 },
308 CellFrequencyLinkSubcell {
309 cell_id_extension: 0x02,
310 transposer_frequency: 0x0DDE_EFF0,
311 },
312 ],
313 },
314 CellFrequencyLinkEntry {
315 cell_id: 0x9999,
316 frequency: 0x4455_6677,
317 subcells: vec![],
318 },
319 ],
320 };
321 let mut buf = vec![0u8; d.serialized_len()];
322 d.serialize_into(&mut buf).unwrap();
323 assert_eq!(CellFrequencyLinkDescriptor::parse(&buf).unwrap(), d);
324 }
325
326 #[test]
327 fn serialize_rejects_too_small_buffer() {
328 let d = CellFrequencyLinkDescriptor {
329 entries: vec![CellFrequencyLinkEntry {
330 cell_id: 0,
331 frequency: 0,
332 subcells: vec![],
333 }],
334 };
335 let mut buf = vec![0u8; 3];
336 assert!(matches!(
337 d.serialize_into(&mut buf).unwrap_err(),
338 Error::OutputBufferTooSmall { .. }
339 ));
340 }
341
342 #[test]
343 fn serialize_rejects_over_range_body() {
344 let d = CellFrequencyLinkDescriptor {
346 entries: (0..37)
347 .map(|_| CellFrequencyLinkEntry {
348 cell_id: 0,
349 frequency: 0,
350 subcells: vec![],
351 })
352 .collect(),
353 };
354 let mut buf = vec![0u8; d.serialized_len()];
355 assert!(matches!(
356 d.serialize_into(&mut buf).unwrap_err(),
357 Error::InvalidDescriptor { tag: TAG, .. }
358 ));
359 }
360
361 #[cfg(feature = "serde")]
362 #[test]
363 fn serde_round_trip() {
364 let d = CellFrequencyLinkDescriptor {
365 entries: vec![CellFrequencyLinkEntry {
366 cell_id: 0x1234,
367 frequency: 0x0011_2233,
368 subcells: vec![CellFrequencyLinkSubcell {
369 cell_id_extension: 0x01,
370 transposer_frequency: 0x0AAB_BCCD,
371 }],
372 }],
373 };
374 let json = serde_json::to_string(&d).unwrap();
375 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
377 }
378}