1use super::descriptor_body;
30use crate::error::{Error, Result};
31use dvb_common::{Parse, Serialize};
32
33pub const TAG: u8 = 0x6C;
35pub const HEADER_LEN: usize = 2;
37pub const OUTER_FIXED_LEN: usize = 10;
40pub const SUBCELL_LEN: usize = 8;
42pub const EXTENT_MASK: u16 = 0x0FFF;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize))]
48pub struct CellListSubcell {
49 pub cell_id_extension: u8,
51 pub subcell_latitude: u16,
53 pub subcell_longitude: u16,
55 pub subcell_extent_of_latitude: u16,
57 pub subcell_extent_of_longitude: u16,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize))]
64pub struct CellListEntry {
65 pub cell_id: u16,
67 pub cell_latitude: u16,
69 pub cell_longitude: u16,
71 pub cell_extent_of_latitude: u16,
73 pub cell_extent_of_longitude: u16,
75 pub subcells: Vec<CellListSubcell>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize))]
82pub struct CellListDescriptor {
83 pub entries: Vec<CellListEntry>,
85}
86
87fn read_extents(b: &[u8]) -> (u16, u16) {
89 let lat = (u16::from(b[0]) << 4) | (u16::from(b[1]) >> 4);
90 let long = ((u16::from(b[1]) & 0x0F) << 8) | u16::from(b[2]);
91 (lat, long)
92}
93
94fn write_extents(buf: &mut [u8], lat: u16, long: u16) {
96 let lat = lat & EXTENT_MASK;
97 let long = long & EXTENT_MASK;
98 buf[0] = (lat >> 4) as u8;
99 buf[1] = (((lat & 0x0F) << 4) | (long >> 8)) as u8;
100 buf[2] = long as u8;
101}
102
103impl<'a> Parse<'a> for CellListDescriptor {
104 type Error = crate::error::Error;
105 fn parse(bytes: &'a [u8]) -> Result<Self> {
106 let body = descriptor_body(
107 bytes,
108 TAG,
109 "CellListDescriptor",
110 "unexpected tag for cell_list_descriptor",
111 )?;
112 let mut entries = Vec::new();
113 let mut pos = 0;
114 while pos < body.len() {
115 if pos + OUTER_FIXED_LEN > body.len() {
116 return Err(Error::InvalidDescriptor {
117 tag: TAG,
118 reason: "cell_list outer entry truncated",
119 });
120 }
121 let cell_id = u16::from_be_bytes([body[pos], body[pos + 1]]);
122 let cell_latitude = u16::from_be_bytes([body[pos + 2], body[pos + 3]]);
123 let cell_longitude = u16::from_be_bytes([body[pos + 4], body[pos + 5]]);
124 let (cell_extent_of_latitude, cell_extent_of_longitude) =
125 read_extents(&body[pos + 6..pos + 9]);
126 let subcell_info_loop_length = body[pos + 9] as usize;
127 pos += OUTER_FIXED_LEN;
128 if subcell_info_loop_length % SUBCELL_LEN != 0 {
129 return Err(Error::InvalidDescriptor {
130 tag: TAG,
131 reason: "subcell_info_loop_length must be a multiple of 8",
132 });
133 }
134 if pos + subcell_info_loop_length > body.len() {
135 return Err(Error::InvalidDescriptor {
136 tag: TAG,
137 reason: "subcell_info_loop_length exceeds descriptor body",
138 });
139 }
140 let subcell_count = subcell_info_loop_length / SUBCELL_LEN;
141 let mut subcells = Vec::with_capacity(subcell_count);
142 for _ in 0..subcell_count {
143 let cell_id_extension = body[pos];
144 let subcell_latitude = u16::from_be_bytes([body[pos + 1], body[pos + 2]]);
145 let subcell_longitude = u16::from_be_bytes([body[pos + 3], body[pos + 4]]);
146 let (subcell_extent_of_latitude, subcell_extent_of_longitude) =
147 read_extents(&body[pos + 5..pos + 8]);
148 subcells.push(CellListSubcell {
149 cell_id_extension,
150 subcell_latitude,
151 subcell_longitude,
152 subcell_extent_of_latitude,
153 subcell_extent_of_longitude,
154 });
155 pos += SUBCELL_LEN;
156 }
157 entries.push(CellListEntry {
158 cell_id,
159 cell_latitude,
160 cell_longitude,
161 cell_extent_of_latitude,
162 cell_extent_of_longitude,
163 subcells,
164 });
165 }
166 Ok(Self { entries })
167 }
168}
169
170impl CellListDescriptor {
171 fn body_len(&self) -> usize {
172 self.entries
173 .iter()
174 .map(|e| OUTER_FIXED_LEN + e.subcells.len() * SUBCELL_LEN)
175 .sum()
176 }
177}
178
179impl Serialize for CellListDescriptor {
180 type Error = crate::error::Error;
181 fn serialized_len(&self) -> usize {
182 HEADER_LEN + self.body_len()
183 }
184
185 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
186 let body_len = self.body_len();
187 if body_len > u8::MAX as usize {
188 return Err(Error::InvalidDescriptor {
189 tag: TAG,
190 reason: "cell_list_descriptor body exceeds 255 bytes",
191 });
192 }
193 for e in &self.entries {
194 if e.subcells.len() * SUBCELL_LEN > u8::MAX as usize {
195 return Err(Error::InvalidDescriptor {
196 tag: TAG,
197 reason: "subcell_info_loop_length exceeds 255 bytes",
198 });
199 }
200 }
201 let len = self.serialized_len();
202 if buf.len() < len {
203 return Err(Error::OutputBufferTooSmall {
204 need: len,
205 have: buf.len(),
206 });
207 }
208 buf[0] = TAG;
209 buf[1] = body_len as u8;
210 let mut pos = HEADER_LEN;
211 for e in &self.entries {
212 buf[pos..pos + 2].copy_from_slice(&e.cell_id.to_be_bytes());
213 buf[pos + 2..pos + 4].copy_from_slice(&e.cell_latitude.to_be_bytes());
214 buf[pos + 4..pos + 6].copy_from_slice(&e.cell_longitude.to_be_bytes());
215 write_extents(
216 &mut buf[pos + 6..pos + 9],
217 e.cell_extent_of_latitude,
218 e.cell_extent_of_longitude,
219 );
220 buf[pos + 9] = (e.subcells.len() * SUBCELL_LEN) as u8;
221 pos += OUTER_FIXED_LEN;
222 for sc in &e.subcells {
223 buf[pos] = sc.cell_id_extension;
224 buf[pos + 1..pos + 3].copy_from_slice(&sc.subcell_latitude.to_be_bytes());
225 buf[pos + 3..pos + 5].copy_from_slice(&sc.subcell_longitude.to_be_bytes());
226 write_extents(
227 &mut buf[pos + 5..pos + 8],
228 sc.subcell_extent_of_latitude,
229 sc.subcell_extent_of_longitude,
230 );
231 pos += SUBCELL_LEN;
232 }
233 }
234 Ok(len)
235 }
236}
237impl<'a> crate::traits::DescriptorDef<'a> for CellListDescriptor {
238 const TAG: u8 = TAG;
239 const NAME: &'static str = "CELL_LIST";
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn extents_pack_round_trips() {
248 let mut b = [0u8; 3];
249 write_extents(&mut b, 0xABC, 0xDEF);
250 assert_eq!(read_extents(&b), (0xABC, 0xDEF));
251 }
252
253 #[test]
254 fn parse_entry_with_subcell() {
255 let bytes = [
256 TAG, 18, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 8,
259 0x07, 0x11, 0x11, 0x22, 0x22, 0x33, 0x34, 0x44,
261 ];
262 let d = CellListDescriptor::parse(&bytes).unwrap();
263 assert_eq!(d.entries.len(), 1);
264 let e = &d.entries[0];
265 assert_eq!(e.cell_id, 0x1234);
266 assert_eq!(e.cell_latitude, 0x5678);
267 assert_eq!(e.cell_longitude, 0x9ABC);
268 assert_eq!(e.cell_extent_of_latitude, 0xDEF);
269 assert_eq!(e.cell_extent_of_longitude, 0x012);
270 assert_eq!(e.subcells.len(), 1);
271 let sc = &e.subcells[0];
272 assert_eq!(sc.cell_id_extension, 0x07);
273 assert_eq!(sc.subcell_latitude, 0x1111);
274 assert_eq!(sc.subcell_longitude, 0x2222);
275 assert_eq!(sc.subcell_extent_of_latitude, 0x333);
276 assert_eq!(sc.subcell_extent_of_longitude, 0x444);
277 }
278
279 #[test]
280 fn parse_entry_no_subcells() {
281 let bytes = [
282 TAG, 10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283 ];
284 let d = CellListDescriptor::parse(&bytes).unwrap();
285 assert_eq!(d.entries.len(), 1);
286 assert!(d.entries[0].subcells.is_empty());
287 }
288
289 #[test]
290 fn empty_body_is_valid() {
291 let d = CellListDescriptor::parse(&[TAG, 0]).unwrap();
292 assert!(d.entries.is_empty());
293 }
294
295 #[test]
296 fn parse_rejects_wrong_tag() {
297 assert!(matches!(
298 CellListDescriptor::parse(&[0x6D, 0]).unwrap_err(),
299 Error::InvalidDescriptor { tag: 0x6D, .. }
300 ));
301 }
302
303 #[test]
304 fn parse_rejects_truncated_outer() {
305 let bytes = [TAG, 5, 0, 0, 0, 0, 0];
306 assert!(matches!(
307 CellListDescriptor::parse(&bytes).unwrap_err(),
308 Error::InvalidDescriptor { tag: TAG, .. }
309 ));
310 }
311
312 #[test]
313 fn parse_rejects_subcell_loop_overrun() {
314 let bytes = [
316 TAG, 10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8,
317 ];
318 assert!(matches!(
319 CellListDescriptor::parse(&bytes).unwrap_err(),
320 Error::InvalidDescriptor { tag: TAG, .. }
321 ));
322 }
323
324 #[test]
325 fn parse_rejects_buffer_shorter_than_length() {
326 let bytes = [TAG, 10, 0x00, 0x01, 0x00];
327 assert!(matches!(
328 CellListDescriptor::parse(&bytes).unwrap_err(),
329 Error::BufferTooShort { .. }
330 ));
331 }
332
333 #[test]
334 fn serialize_round_trip() {
335 let d = CellListDescriptor {
336 entries: vec![
337 CellListEntry {
338 cell_id: 0x1234,
339 cell_latitude: 0x5678,
340 cell_longitude: 0x9ABC,
341 cell_extent_of_latitude: 0xDEF,
342 cell_extent_of_longitude: 0x012,
343 subcells: vec![CellListSubcell {
344 cell_id_extension: 0x07,
345 subcell_latitude: 0x1111,
346 subcell_longitude: 0x2222,
347 subcell_extent_of_latitude: 0x333,
348 subcell_extent_of_longitude: 0x444,
349 }],
350 },
351 CellListEntry {
352 cell_id: 0xAAAA,
353 cell_latitude: 0xBBBB,
354 cell_longitude: 0xCCCC,
355 cell_extent_of_latitude: 0x111,
356 cell_extent_of_longitude: 0x222,
357 subcells: vec![],
358 },
359 ],
360 };
361 let mut buf = vec![0u8; d.serialized_len()];
362 d.serialize_into(&mut buf).unwrap();
363 assert_eq!(CellListDescriptor::parse(&buf).unwrap(), d);
364 }
365
366 #[test]
367 fn serialize_rejects_too_small_buffer() {
368 let d = CellListDescriptor {
369 entries: vec![CellListEntry {
370 cell_id: 0,
371 cell_latitude: 0,
372 cell_longitude: 0,
373 cell_extent_of_latitude: 0,
374 cell_extent_of_longitude: 0,
375 subcells: vec![],
376 }],
377 };
378 let mut buf = vec![0u8; 3];
379 assert!(matches!(
380 d.serialize_into(&mut buf).unwrap_err(),
381 Error::OutputBufferTooSmall { .. }
382 ));
383 }
384
385 #[test]
386 fn serialize_rejects_over_range_body() {
387 let d = CellListDescriptor {
389 entries: (0..26)
390 .map(|_| CellListEntry {
391 cell_id: 0,
392 cell_latitude: 0,
393 cell_longitude: 0,
394 cell_extent_of_latitude: 0,
395 cell_extent_of_longitude: 0,
396 subcells: vec![],
397 })
398 .collect(),
399 };
400 let mut buf = vec![0u8; d.serialized_len()];
401 assert!(matches!(
402 d.serialize_into(&mut buf).unwrap_err(),
403 Error::InvalidDescriptor { tag: TAG, .. }
404 ));
405 }
406
407 #[cfg(feature = "serde")]
408 #[test]
409 fn serde_round_trip() {
410 let d = CellListDescriptor {
411 entries: vec![CellListEntry {
412 cell_id: 0x1234,
413 cell_latitude: 0x5678,
414 cell_longitude: 0x9ABC,
415 cell_extent_of_latitude: 0xDEF,
416 cell_extent_of_longitude: 0x012,
417 subcells: vec![CellListSubcell {
418 cell_id_extension: 0x07,
419 subcell_latitude: 0x1111,
420 subcell_longitude: 0x2222,
421 subcell_extent_of_latitude: 0x333,
422 subcell_extent_of_longitude: 0x444,
423 }],
424 }],
425 };
426 let json = serde_json::to_string(&d).unwrap();
427 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
429 }
430}