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