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