1use crate::descriptors::DescriptorLoop;
24use crate::error::{Error, Result};
25use crate::traits::Table;
26use dvb_common::{Parse, Serialize};
27
28pub const TABLE_ID: u8 = 0x4C;
30
31pub const PID: u16 = 0x0000;
38
39pub const ACTION_TYPE_STREAM_ANNOUNCEMENT: u8 = 0x01;
41
42const OUTER_HEADER_LEN: usize = 3;
46
47const INT_FIXED_LEN: usize = 9;
52
53const LOOP_LEN_FIELD: usize = 2;
55
56const CRC_LEN: usize = 4;
58
59const MIN_SECTION_LEN: usize = OUTER_HEADER_LEN + INT_FIXED_LEN + LOOP_LEN_FIELD + CRC_LEN;
62
63const OFF_ACTION_TYPE: usize = 3;
67const OFF_PLATFORM_ID_HASH: usize = 4;
69const OFF_VERSION_BYTE: usize = 5;
71const OFF_SECTION_NUMBER: usize = 6;
73const OFF_LAST_SECTION_NUMBER: usize = 7;
75const OFF_PLATFORM_ID: usize = 8;
77const OFF_PROCESSING_ORDER: usize = 11;
79const OFF_PLATFORM_DESC_LEN: usize = 12;
81
82#[derive(Debug, Clone, PartialEq, Eq)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize))]
94pub struct Int<'a> {
95 pub action_type: u8,
97
98 pub platform_id_hash: u8,
102
103 pub version_number: u8,
105
106 pub current_next_indicator: bool,
109
110 pub section_number: u8,
112
113 pub last_section_number: u8,
115
116 pub platform_id: u32,
119
120 pub processing_order: u8,
123
124 pub platform_descriptors: DescriptorLoop<'a>,
128
129 #[cfg_attr(feature = "serde", serde(borrow))]
138 pub loops: &'a [u8],
139}
140
141impl<'a> Parse<'a> for Int<'a> {
144 type Error = crate::error::Error;
145
146 fn parse(bytes: &'a [u8]) -> Result<Self> {
147 if bytes.len() < MIN_SECTION_LEN {
149 return Err(Error::BufferTooShort {
150 need: MIN_SECTION_LEN,
151 have: bytes.len(),
152 what: "Int",
153 });
154 }
155
156 if bytes[0] != TABLE_ID {
158 return Err(Error::UnexpectedTableId {
159 table_id: bytes[0],
160 what: "Int",
161 expected: &[TABLE_ID],
162 });
163 }
164
165 let section_length = (((bytes[1] & 0x0F) as usize) << 8) | bytes[2] as usize;
167 let total = OUTER_HEADER_LEN + section_length;
168 if bytes.len() < total {
169 return Err(Error::SectionLengthOverflow {
170 declared: section_length,
171 available: bytes.len() - OUTER_HEADER_LEN,
172 });
173 }
174
175 let action_type = bytes[OFF_ACTION_TYPE];
177 let platform_id_hash = bytes[OFF_PLATFORM_ID_HASH];
178 let version_byte = bytes[OFF_VERSION_BYTE];
179 let version_number = (version_byte >> 1) & 0x1F;
180 let current_next_indicator = (version_byte & 0x01) != 0;
181 let section_number = bytes[OFF_SECTION_NUMBER];
182 let last_section_number = bytes[OFF_LAST_SECTION_NUMBER];
183 let platform_id = ((bytes[OFF_PLATFORM_ID] as u32) << 16)
184 | ((bytes[OFF_PLATFORM_ID + 1] as u32) << 8)
185 | bytes[OFF_PLATFORM_ID + 2] as u32;
186 let processing_order = bytes[OFF_PROCESSING_ORDER];
187
188 let plat_desc_len = (((bytes[OFF_PLATFORM_DESC_LEN] & 0x0F) as usize) << 8)
190 | bytes[OFF_PLATFORM_DESC_LEN + 1] as usize;
191
192 let plat_desc_start = OFF_PLATFORM_DESC_LEN + LOOP_LEN_FIELD;
193 let plat_desc_end = plat_desc_start + plat_desc_len;
194
195 if plat_desc_end > total - CRC_LEN {
197 return Err(Error::SectionLengthOverflow {
198 declared: plat_desc_len,
199 available: (total - CRC_LEN).saturating_sub(plat_desc_start),
200 });
201 }
202
203 let platform_descriptors = DescriptorLoop::new(&bytes[plat_desc_start..plat_desc_end]);
204
205 let loops_start = plat_desc_end;
208 let loops_end = total - CRC_LEN;
209 let loops = &bytes[loops_start..loops_end];
210
211 Ok(Int {
212 action_type,
213 platform_id_hash,
214 version_number,
215 current_next_indicator,
216 section_number,
217 last_section_number,
218 platform_id,
219 processing_order,
220 platform_descriptors,
221 loops,
222 })
223 }
224}
225
226impl Serialize for Int<'_> {
229 type Error = crate::error::Error;
230
231 fn serialized_len(&self) -> usize {
232 OUTER_HEADER_LEN
233 + INT_FIXED_LEN
234 + LOOP_LEN_FIELD + self.platform_descriptors.len()
236 + self.loops.len()
237 + CRC_LEN
238 }
239
240 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
241 let len = self.serialized_len();
242 if buf.len() < len {
243 return Err(Error::OutputBufferTooSmall {
244 need: len,
245 have: buf.len(),
246 });
247 }
248
249 let section_length = (len - OUTER_HEADER_LEN) as u16;
251 buf[0] = TABLE_ID;
252 buf[1] = 0xF0 | ((section_length >> 8) as u8 & 0x0F);
254 buf[2] = (section_length & 0xFF) as u8;
255
256 buf[OFF_ACTION_TYPE] = self.action_type;
258 buf[OFF_PLATFORM_ID_HASH] = self.platform_id_hash;
259 buf[OFF_VERSION_BYTE] =
260 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
261 buf[OFF_SECTION_NUMBER] = self.section_number;
262 buf[OFF_LAST_SECTION_NUMBER] = self.last_section_number;
263 buf[OFF_PLATFORM_ID] = ((self.platform_id >> 16) & 0xFF) as u8;
265 buf[OFF_PLATFORM_ID + 1] = ((self.platform_id >> 8) & 0xFF) as u8;
266 buf[OFF_PLATFORM_ID + 2] = (self.platform_id & 0xFF) as u8;
267 buf[OFF_PROCESSING_ORDER] = self.processing_order;
268
269 let pdl = self.platform_descriptors.len() as u16;
271 buf[OFF_PLATFORM_DESC_LEN] = 0xF0 | ((pdl >> 8) as u8 & 0x0F);
272 buf[OFF_PLATFORM_DESC_LEN + 1] = (pdl & 0xFF) as u8;
273
274 let plat_start = OFF_PLATFORM_DESC_LEN + LOOP_LEN_FIELD;
276 let plat_end = plat_start + self.platform_descriptors.len();
277 buf[plat_start..plat_end].copy_from_slice(self.platform_descriptors.raw());
278
279 let loops_start = plat_end;
281 let loops_end = loops_start + self.loops.len();
282 buf[loops_start..loops_end].copy_from_slice(self.loops);
283
284 let crc_pos = len - CRC_LEN;
286 let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
287 buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
288
289 Ok(len)
290 }
291}
292
293impl<'a> Table<'a> for Int<'a> {
296 const TABLE_ID: u8 = TABLE_ID;
297 const PID: u16 = PID;
298}
299
300impl<'a> crate::traits::TableDef<'a> for Int<'a> {
301 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
302 const NAME: &'static str = "IP_MAC_NOTIFICATION";
303}
304
305#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[allow(clippy::too_many_arguments)]
317 fn build_int(
318 action_type: u8,
319 platform_id_hash: u8,
320 version_number: u8,
321 current_next_indicator: bool,
322 section_number: u8,
323 last_section_number: u8,
324 platform_id: u32,
325 processing_order: u8,
326 platform_desc: &[u8],
327 loops: &[u8],
328 ) -> Vec<u8> {
329 let int = Int {
330 action_type,
331 platform_id_hash,
332 version_number,
333 current_next_indicator,
334 section_number,
335 last_section_number,
336 platform_id,
337 processing_order,
338 platform_descriptors: DescriptorLoop::new(platform_desc),
339 loops,
340 };
341 let mut buf = vec![0u8; int.serialized_len()];
342 int.serialize_into(&mut buf).unwrap();
343 buf
344 }
345
346 #[test]
347 fn parse_happy_path() {
348 let bytes = build_int(
350 ACTION_TYPE_STREAM_ANNOUNCEMENT,
351 0x12 ^ 0x34, 3,
354 true,
355 0,
356 0,
357 0x00_12_34,
358 0x00,
359 &[0x81, 0x02, 0xAB, 0xCD],
360 &[],
361 );
362 let int = Int::parse(&bytes).unwrap();
363
364 assert_eq!(int.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
365 assert_eq!(int.platform_id_hash, 0x12 ^ 0x34);
366 assert_eq!(int.version_number, 3);
367 assert!(int.current_next_indicator);
368 assert_eq!(int.section_number, 0);
369 assert_eq!(int.last_section_number, 0);
370 assert_eq!(int.platform_id, 0x00_12_34);
371 assert_eq!(int.processing_order, 0x00);
372 assert_eq!(
373 int.platform_descriptors.raw(),
374 &[0x81, 0x02, 0xAB, 0xCD][..]
375 );
376 assert_eq!(int.loops, &[] as &[u8]);
377 }
378
379 #[test]
380 fn parse_happy_path_with_loops() {
381 let fake_loops: [u8; 4] = [
384 0xF0, 0x00, 0xF0, 0x00, ];
387 let bytes = build_int(
388 0x01,
389 0x56,
390 5,
391 false,
392 1,
393 1,
394 0x00_56_78,
395 0x01,
396 &[],
397 &fake_loops,
398 );
399 let int = Int::parse(&bytes).unwrap();
400 assert_eq!(int.platform_id, 0x00_56_78);
401 assert_eq!(int.version_number, 5);
402 assert!(!int.current_next_indicator);
403 assert_eq!(int.loops, &fake_loops[..]);
404 }
405
406 #[test]
407 fn parse_rejects_wrong_table_id() {
408 let mut bytes = build_int(0x01, 0x00, 0, true, 0, 0, 0x000001, 0x00, &[], &[]);
409 bytes[0] = 0x4B; let err = Int::parse(&bytes).unwrap_err();
411 assert!(matches!(
412 err,
413 Error::UnexpectedTableId { table_id: 0x4B, .. }
414 ));
415 }
416
417 #[test]
418 fn parse_rejects_buffer_too_short() {
419 let err = Int::parse(&[TABLE_ID, 0xF0]).unwrap_err();
420 assert!(matches!(err, Error::BufferTooShort { what: "Int", .. }));
421 }
422
423 #[test]
424 fn serialize_round_trip() {
425 let plat_desc = [0x7C, 0x04, 0x01, 0x02, 0x03, 0x04];
426 let fake_loops: [u8; 4] = [0xF0, 0x00, 0xF0, 0x00];
427 let bytes = build_int(
428 ACTION_TYPE_STREAM_ANNOUNCEMENT,
429 0xAB,
430 15,
431 true,
432 2,
433 3,
434 0x00_AB_CD,
435 0x00,
436 &plat_desc,
437 &fake_loops,
438 );
439
440 let int = Int::parse(&bytes).unwrap();
441
442 let mut buf = vec![0u8; int.serialized_len()];
444 int.serialize_into(&mut buf).unwrap();
445
446 let re = Int::parse(&buf).unwrap();
448
449 assert_eq!(int, re);
450 assert_eq!(re.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
451 assert_eq!(re.platform_id_hash, 0xAB);
452 assert_eq!(re.version_number, 15);
453 assert!(re.current_next_indicator);
454 assert_eq!(re.section_number, 2);
455 assert_eq!(re.last_section_number, 3);
456 assert_eq!(re.platform_id, 0x00_AB_CD);
457 assert_eq!(re.processing_order, 0x00);
458 assert_eq!(re.platform_descriptors.raw(), &plat_desc[..]);
459 assert_eq!(re.loops, &fake_loops[..]);
460 }
461
462 #[test]
463 fn serialize_rejects_too_small_output_buffer() {
464 let int = Int {
465 action_type: 0x01,
466 platform_id_hash: 0x00,
467 version_number: 0,
468 current_next_indicator: true,
469 section_number: 0,
470 last_section_number: 0,
471 platform_id: 0,
472 processing_order: 0,
473 platform_descriptors: DescriptorLoop::new(&[]),
474 loops: &[],
475 };
476 let mut buf = vec![0u8; 2]; let err = int.serialize_into(&mut buf).unwrap_err();
478 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
479 }
480
481 #[test]
482 fn platform_id_24bit_boundary() {
483 let bytes = build_int(0x01, 0xFF, 0, true, 0, 0, 0x00FF_FFFF, 0x00, &[], &[]);
485 let int = Int::parse(&bytes).unwrap();
486 assert_eq!(int.platform_id, 0x00FF_FFFF);
487 }
488}