1use super::descriptor_body;
29use crate::error::{Error, Result};
30use alloc::vec::Vec;
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
45const LAT_SCALE: f64 = 90.0 / 32768.0;
47const LONG_SCALE: f64 = 180.0 / 32768.0;
49
50fn u16_to_i16(raw: u16) -> i16 {
52 raw as i16
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize))]
58pub struct CellListSubcell {
59 pub cell_id_extension: u8,
61 pub subcell_latitude: u16,
63 pub subcell_longitude: u16,
65 pub subcell_extent_of_latitude: u16,
67 pub subcell_extent_of_longitude: u16,
69}
70
71impl CellListSubcell {
72 #[must_use]
74 pub fn subcell_latitude_deg(&self) -> f64 {
75 u16_to_i16(self.subcell_latitude) as f64 * LAT_SCALE
76 }
77
78 #[must_use]
80 pub fn subcell_longitude_deg(&self) -> f64 {
81 u16_to_i16(self.subcell_longitude) as f64 * LONG_SCALE
82 }
83
84 #[must_use]
86 pub fn subcell_extent_of_latitude_deg(&self) -> f64 {
87 (self.subcell_extent_of_latitude & EXTENT_MASK) as f64 * LAT_SCALE
88 }
89
90 #[must_use]
92 pub fn subcell_extent_of_longitude_deg(&self) -> f64 {
93 (self.subcell_extent_of_longitude & EXTENT_MASK) as f64 * LONG_SCALE
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize))]
100pub struct CellListEntry {
101 pub cell_id: u16,
103 pub cell_latitude: u16,
105 pub cell_longitude: u16,
107 pub cell_extent_of_latitude: u16,
109 pub cell_extent_of_longitude: u16,
111 pub subcells: Vec<CellListSubcell>,
113}
114
115impl CellListEntry {
116 #[must_use]
118 pub fn cell_latitude_deg(&self) -> f64 {
119 u16_to_i16(self.cell_latitude) as f64 * LAT_SCALE
120 }
121
122 #[must_use]
124 pub fn cell_longitude_deg(&self) -> f64 {
125 u16_to_i16(self.cell_longitude) as f64 * LONG_SCALE
126 }
127
128 #[must_use]
130 pub fn cell_extent_of_latitude_deg(&self) -> f64 {
131 (self.cell_extent_of_latitude & EXTENT_MASK) as f64 * LAT_SCALE
132 }
133
134 #[must_use]
136 pub fn cell_extent_of_longitude_deg(&self) -> f64 {
137 (self.cell_extent_of_longitude & EXTENT_MASK) as f64 * LONG_SCALE
138 }
139}
140
141#[derive(Debug, Clone, PartialEq, Eq)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize))]
144pub struct CellListDescriptor {
145 pub entries: Vec<CellListEntry>,
147}
148
149fn read_extents(b: &[u8]) -> (u16, u16) {
151 let lat = (u16::from(b[0]) << 4) | (u16::from(b[1]) >> 4);
152 let long = ((u16::from(b[1]) & 0x0F) << 8) | u16::from(b[2]);
153 (lat, long)
154}
155
156fn write_extents(buf: &mut [u8], lat: u16, long: u16) {
158 let lat = lat & EXTENT_MASK;
159 let long = long & EXTENT_MASK;
160 buf[0] = (lat >> 4) as u8;
161 buf[1] = (((lat & 0x0F) << 4) | (long >> 8)) as u8;
162 buf[2] = long as u8;
163}
164
165impl<'a> Parse<'a> for CellListDescriptor {
166 type Error = crate::error::Error;
167 fn parse(bytes: &'a [u8]) -> Result<Self> {
168 let body = descriptor_body(
169 bytes,
170 TAG,
171 "CellListDescriptor",
172 "unexpected tag for cell_list_descriptor",
173 )?;
174 let mut entries = Vec::new();
175 let mut pos = 0;
176 while pos < body.len() {
177 if pos + OUTER_FIXED_LEN > body.len() {
178 return Err(Error::InvalidDescriptor {
179 tag: TAG,
180 reason: "cell_list outer entry truncated",
181 });
182 }
183 let (outer, _) = body
184 .get(pos..)
185 .and_then(|s| s.split_first_chunk::<OUTER_FIXED_LEN>())
186 .ok_or(Error::InvalidDescriptor {
187 tag: TAG,
188 reason: "cell_list outer entry truncated",
189 })?;
190 let cell_id = u16::from_be_bytes([outer[0], outer[1]]);
191 let cell_latitude = u16::from_be_bytes([outer[2], outer[3]]);
192 let cell_longitude = u16::from_be_bytes([outer[4], outer[5]]);
193 let (cell_extent_of_latitude, cell_extent_of_longitude) = read_extents(&outer[6..9]);
194 let subcell_info_loop_length = outer[9] as usize;
195 pos += OUTER_FIXED_LEN;
196 if subcell_info_loop_length % SUBCELL_LEN != 0 {
197 return Err(Error::InvalidDescriptor {
198 tag: TAG,
199 reason: "subcell_info_loop_length must be a multiple of 8",
200 });
201 }
202 if pos + subcell_info_loop_length > body.len() {
203 return Err(Error::InvalidDescriptor {
204 tag: TAG,
205 reason: "subcell_info_loop_length exceeds descriptor body",
206 });
207 }
208 let subcell_count = subcell_info_loop_length / SUBCELL_LEN;
209 let mut subcells = Vec::with_capacity(subcell_count);
210 for _ in 0..subcell_count {
211 let (sub, _) = body
212 .get(pos..)
213 .and_then(|s| s.split_first_chunk::<SUBCELL_LEN>())
214 .ok_or(Error::InvalidDescriptor {
215 tag: TAG,
216 reason: "subcell_info_loop_length exceeds descriptor body",
217 })?;
218 let cell_id_extension = sub[0];
219 let subcell_latitude = u16::from_be_bytes([sub[1], sub[2]]);
220 let subcell_longitude = u16::from_be_bytes([sub[3], sub[4]]);
221 let (subcell_extent_of_latitude, subcell_extent_of_longitude) =
222 read_extents(&sub[5..8]);
223 subcells.push(CellListSubcell {
224 cell_id_extension,
225 subcell_latitude,
226 subcell_longitude,
227 subcell_extent_of_latitude,
228 subcell_extent_of_longitude,
229 });
230 pos += SUBCELL_LEN;
231 }
232 entries.push(CellListEntry {
233 cell_id,
234 cell_latitude,
235 cell_longitude,
236 cell_extent_of_latitude,
237 cell_extent_of_longitude,
238 subcells,
239 });
240 }
241 Ok(Self { entries })
242 }
243}
244
245impl CellListDescriptor {
246 fn body_len(&self) -> usize {
247 self.entries
248 .iter()
249 .map(|e| OUTER_FIXED_LEN + e.subcells.len() * SUBCELL_LEN)
250 .sum()
251 }
252}
253
254impl Serialize for CellListDescriptor {
255 type Error = crate::error::Error;
256 fn serialized_len(&self) -> usize {
257 HEADER_LEN + self.body_len()
258 }
259
260 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
261 let body_len = self.body_len();
262 if body_len > u8::MAX as usize {
263 return Err(Error::InvalidDescriptor {
264 tag: TAG,
265 reason: "cell_list_descriptor body exceeds 255 bytes",
266 });
267 }
268 for e in &self.entries {
269 if e.subcells.len() * SUBCELL_LEN > u8::MAX as usize {
270 return Err(Error::InvalidDescriptor {
271 tag: TAG,
272 reason: "subcell_info_loop_length exceeds 255 bytes",
273 });
274 }
275 }
276 let len = self.serialized_len();
277 if buf.len() < len {
278 return Err(Error::OutputBufferTooSmall {
279 need: len,
280 have: buf.len(),
281 });
282 }
283 buf[0] = TAG;
284 buf[1] = body_len as u8;
285 let mut pos = HEADER_LEN;
286 for e in &self.entries {
287 buf[pos..pos + 2].copy_from_slice(&e.cell_id.to_be_bytes());
288 buf[pos + 2..pos + 4].copy_from_slice(&e.cell_latitude.to_be_bytes());
289 buf[pos + 4..pos + 6].copy_from_slice(&e.cell_longitude.to_be_bytes());
290 write_extents(
291 &mut buf[pos + 6..pos + 9],
292 e.cell_extent_of_latitude,
293 e.cell_extent_of_longitude,
294 );
295 buf[pos + 9] = (e.subcells.len() * SUBCELL_LEN) as u8;
296 pos += OUTER_FIXED_LEN;
297 for sc in &e.subcells {
298 buf[pos] = sc.cell_id_extension;
299 buf[pos + 1..pos + 3].copy_from_slice(&sc.subcell_latitude.to_be_bytes());
300 buf[pos + 3..pos + 5].copy_from_slice(&sc.subcell_longitude.to_be_bytes());
301 write_extents(
302 &mut buf[pos + 5..pos + 8],
303 sc.subcell_extent_of_latitude,
304 sc.subcell_extent_of_longitude,
305 );
306 pos += SUBCELL_LEN;
307 }
308 }
309 Ok(len)
310 }
311}
312impl<'a> crate::traits::DescriptorDef<'a> for CellListDescriptor {
313 const TAG: u8 = TAG;
314 const NAME: &'static str = "CELL_LIST";
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn extents_pack_round_trips() {
323 let mut b = [0u8; 3];
324 write_extents(&mut b, 0xABC, 0xDEF);
325 assert_eq!(read_extents(&b), (0xABC, 0xDEF));
326 }
327
328 #[test]
329 fn cell_latitude_deg_zero() {
330 let e = CellListEntry {
331 cell_id: 0,
332 cell_latitude: 0,
333 cell_longitude: 0,
334 cell_extent_of_latitude: 0,
335 cell_extent_of_longitude: 0,
336 subcells: vec![],
337 };
338 assert_eq!(e.cell_latitude_deg(), 0.0);
339 assert_eq!(e.cell_longitude_deg(), 0.0);
340 }
341
342 #[test]
343 fn cell_latitude_deg_max() {
344 let e = CellListEntry {
346 cell_id: 0,
347 cell_latitude: 0x7FFF,
348 cell_longitude: 0,
349 cell_extent_of_latitude: 0,
350 cell_extent_of_longitude: 0,
351 subcells: vec![],
352 };
353 let deg = e.cell_latitude_deg();
354 assert!(deg > 89.9 && deg <= 90.0, "got {deg}");
355 }
356
357 #[test]
358 fn cell_longitude_deg_max() {
359 let e = CellListEntry {
360 cell_id: 0,
361 cell_latitude: 0,
362 cell_longitude: 0x7FFF,
363 cell_extent_of_latitude: 0,
364 cell_extent_of_longitude: 0,
365 subcells: vec![],
366 };
367 let deg = e.cell_longitude_deg();
368 assert!(deg > 179.9 && deg <= 180.0, "got {deg}");
369 }
370
371 #[test]
372 fn cell_extent_deg() {
373 let e = CellListEntry {
375 cell_id: 0,
376 cell_latitude: 0,
377 cell_longitude: 0,
378 cell_extent_of_latitude: 4095,
379 cell_extent_of_longitude: 2048,
380 subcells: vec![],
381 };
382 assert!(e.cell_extent_of_latitude_deg() > 11.2);
383 assert!(e.cell_extent_of_longitude_deg() > 11.2);
384 }
385
386 #[test]
387 fn parse_entry_with_subcell() {
388 let bytes = [
389 TAG, 18, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 8,
392 0x07, 0x11, 0x11, 0x22, 0x22, 0x33, 0x34, 0x44,
394 ];
395 let d = CellListDescriptor::parse(&bytes).unwrap();
396 assert_eq!(d.entries.len(), 1);
397 let e = &d.entries[0];
398 assert_eq!(e.cell_id, 0x1234);
399 assert_eq!(e.cell_latitude, 0x5678);
400 assert_eq!(e.cell_longitude, 0x9ABC);
401 assert_eq!(e.cell_extent_of_latitude, 0xDEF);
402 assert_eq!(e.cell_extent_of_longitude, 0x012);
403 assert_eq!(e.subcells.len(), 1);
404 let sc = &e.subcells[0];
405 assert_eq!(sc.cell_id_extension, 0x07);
406 assert_eq!(sc.subcell_latitude, 0x1111);
407 assert_eq!(sc.subcell_longitude, 0x2222);
408 assert_eq!(sc.subcell_extent_of_latitude, 0x333);
409 assert_eq!(sc.subcell_extent_of_longitude, 0x444);
410
411 let lat_deg = e.cell_latitude_deg();
413 let long_deg = e.cell_longitude_deg();
414 assert!(lat_deg.abs() > 0.0);
415 assert!(long_deg.abs() > 0.0);
416 let sc_lat = sc.subcell_latitude_deg();
417 let sc_long = sc.subcell_longitude_deg();
418 assert!(sc_lat.abs() > 0.0);
419 assert!(sc_long.abs() > 0.0);
420 }
421
422 #[test]
423 fn parse_entry_no_subcells() {
424 let bytes = [
425 TAG, 10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426 ];
427 let d = CellListDescriptor::parse(&bytes).unwrap();
428 assert_eq!(d.entries.len(), 1);
429 assert!(d.entries[0].subcells.is_empty());
430 }
431
432 #[test]
433 fn empty_body_is_valid() {
434 let d = CellListDescriptor::parse(&[TAG, 0]).unwrap();
435 assert!(d.entries.is_empty());
436 }
437
438 #[test]
439 fn parse_rejects_wrong_tag() {
440 assert!(matches!(
441 CellListDescriptor::parse(&[0x6D, 0]).unwrap_err(),
442 Error::InvalidDescriptor { tag: 0x6D, .. }
443 ));
444 }
445
446 #[test]
447 fn parse_rejects_truncated_outer() {
448 let bytes = [TAG, 5, 0, 0, 0, 0, 0];
449 assert!(matches!(
450 CellListDescriptor::parse(&bytes).unwrap_err(),
451 Error::InvalidDescriptor { tag: TAG, .. }
452 ));
453 }
454
455 #[test]
456 fn parse_rejects_subcell_loop_overrun() {
457 let bytes = [
459 TAG, 10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8,
460 ];
461 assert!(matches!(
462 CellListDescriptor::parse(&bytes).unwrap_err(),
463 Error::InvalidDescriptor { tag: TAG, .. }
464 ));
465 }
466
467 #[test]
468 fn parse_rejects_buffer_shorter_than_length() {
469 let bytes = [TAG, 10, 0x00, 0x01, 0x00];
470 assert!(matches!(
471 CellListDescriptor::parse(&bytes).unwrap_err(),
472 Error::BufferTooShort { .. }
473 ));
474 }
475
476 #[test]
477 fn serialize_round_trip() {
478 let d = CellListDescriptor {
479 entries: vec![
480 CellListEntry {
481 cell_id: 0x1234,
482 cell_latitude: 0x5678,
483 cell_longitude: 0x9ABC,
484 cell_extent_of_latitude: 0xDEF,
485 cell_extent_of_longitude: 0x012,
486 subcells: vec![CellListSubcell {
487 cell_id_extension: 0x07,
488 subcell_latitude: 0x1111,
489 subcell_longitude: 0x2222,
490 subcell_extent_of_latitude: 0x333,
491 subcell_extent_of_longitude: 0x444,
492 }],
493 },
494 CellListEntry {
495 cell_id: 0xAAAA,
496 cell_latitude: 0xBBBB,
497 cell_longitude: 0xCCCC,
498 cell_extent_of_latitude: 0x111,
499 cell_extent_of_longitude: 0x222,
500 subcells: vec![],
501 },
502 ],
503 };
504 let mut buf = vec![0u8; d.serialized_len()];
505 d.serialize_into(&mut buf).unwrap();
506 assert_eq!(CellListDescriptor::parse(&buf).unwrap(), d);
507 }
508
509 #[test]
510 fn serialize_rejects_too_small_buffer() {
511 let d = CellListDescriptor {
512 entries: vec![CellListEntry {
513 cell_id: 0,
514 cell_latitude: 0,
515 cell_longitude: 0,
516 cell_extent_of_latitude: 0,
517 cell_extent_of_longitude: 0,
518 subcells: vec![],
519 }],
520 };
521 let mut buf = vec![0u8; 3];
522 assert!(matches!(
523 d.serialize_into(&mut buf).unwrap_err(),
524 Error::OutputBufferTooSmall { .. }
525 ));
526 }
527
528 #[test]
529 fn serialize_rejects_over_range_body() {
530 let d = CellListDescriptor {
532 entries: (0..26)
533 .map(|_| CellListEntry {
534 cell_id: 0,
535 cell_latitude: 0,
536 cell_longitude: 0,
537 cell_extent_of_latitude: 0,
538 cell_extent_of_longitude: 0,
539 subcells: vec![],
540 })
541 .collect(),
542 };
543 let mut buf = vec![0u8; d.serialized_len()];
544 assert!(matches!(
545 d.serialize_into(&mut buf).unwrap_err(),
546 Error::InvalidDescriptor { tag: TAG, .. }
547 ));
548 }
549
550 #[cfg(feature = "serde")]
551 #[test]
552 fn serde_round_trip() {
553 let d = CellListDescriptor {
554 entries: vec![CellListEntry {
555 cell_id: 0x1234,
556 cell_latitude: 0x5678,
557 cell_longitude: 0x9ABC,
558 cell_extent_of_latitude: 0xDEF,
559 cell_extent_of_longitude: 0x012,
560 subcells: vec![CellListSubcell {
561 cell_id_extension: 0x07,
562 subcell_latitude: 0x1111,
563 subcell_longitude: 0x2222,
564 subcell_extent_of_latitude: 0x333,
565 subcell_extent_of_longitude: 0x444,
566 }],
567 }],
568 };
569 let json = serde_json::to_string(&d).unwrap();
570 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
572 }
573}