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 pub loops: &'a [u8],
138}
139
140impl<'a> Parse<'a> for Int<'a> {
143 type Error = crate::error::Error;
144
145 fn parse(bytes: &'a [u8]) -> Result<Self> {
146 if bytes.len() < MIN_SECTION_LEN {
148 return Err(Error::BufferTooShort {
149 need: MIN_SECTION_LEN,
150 have: bytes.len(),
151 what: "Int",
152 });
153 }
154
155 if bytes[0] != TABLE_ID {
157 return Err(Error::UnexpectedTableId {
158 table_id: bytes[0],
159 what: "Int",
160 expected: &[TABLE_ID],
161 });
162 }
163
164 let section_length = (((bytes[1] & 0x0F) as usize) << 8) | bytes[2] as usize;
166 let total = OUTER_HEADER_LEN + section_length;
167 if bytes.len() < total {
168 return Err(Error::SectionLengthOverflow {
169 declared: section_length,
170 available: bytes.len() - OUTER_HEADER_LEN,
171 });
172 }
173
174 let action_type = bytes[OFF_ACTION_TYPE];
176 let platform_id_hash = bytes[OFF_PLATFORM_ID_HASH];
177 let version_byte = bytes[OFF_VERSION_BYTE];
178 let version_number = (version_byte >> 1) & 0x1F;
179 let current_next_indicator = (version_byte & 0x01) != 0;
180 let section_number = bytes[OFF_SECTION_NUMBER];
181 let last_section_number = bytes[OFF_LAST_SECTION_NUMBER];
182 let platform_id = ((bytes[OFF_PLATFORM_ID] as u32) << 16)
183 | ((bytes[OFF_PLATFORM_ID + 1] as u32) << 8)
184 | bytes[OFF_PLATFORM_ID + 2] as u32;
185 let processing_order = bytes[OFF_PROCESSING_ORDER];
186
187 let plat_desc_len = (((bytes[OFF_PLATFORM_DESC_LEN] & 0x0F) as usize) << 8)
189 | bytes[OFF_PLATFORM_DESC_LEN + 1] as usize;
190
191 let plat_desc_start = OFF_PLATFORM_DESC_LEN + LOOP_LEN_FIELD;
192 let plat_desc_end = plat_desc_start + plat_desc_len;
193
194 if plat_desc_end > total - CRC_LEN {
196 return Err(Error::SectionLengthOverflow {
197 declared: plat_desc_len,
198 available: (total - CRC_LEN).saturating_sub(plat_desc_start),
199 });
200 }
201
202 let platform_descriptors = DescriptorLoop::new(&bytes[plat_desc_start..plat_desc_end]);
203
204 let loops_start = plat_desc_end;
207 let loops_end = total - CRC_LEN;
208 let loops = &bytes[loops_start..loops_end];
209
210 Ok(Int {
211 action_type,
212 platform_id_hash,
213 version_number,
214 current_next_indicator,
215 section_number,
216 last_section_number,
217 platform_id,
218 processing_order,
219 platform_descriptors,
220 loops,
221 })
222 }
223}
224
225impl Serialize for Int<'_> {
228 type Error = crate::error::Error;
229
230 fn serialized_len(&self) -> usize {
231 OUTER_HEADER_LEN
232 + INT_FIXED_LEN
233 + LOOP_LEN_FIELD + self.platform_descriptors.len()
235 + self.loops.len()
236 + CRC_LEN
237 }
238
239 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
240 let len = self.serialized_len();
241 if buf.len() < len {
242 return Err(Error::OutputBufferTooSmall {
243 need: len,
244 have: buf.len(),
245 });
246 }
247
248 let section_length = (len - OUTER_HEADER_LEN) as u16;
250 buf[0] = TABLE_ID;
251 buf[1] = 0xF0 | ((section_length >> 8) as u8 & 0x0F);
253 buf[2] = (section_length & 0xFF) as u8;
254
255 buf[OFF_ACTION_TYPE] = self.action_type;
257 buf[OFF_PLATFORM_ID_HASH] = self.platform_id_hash;
258 buf[OFF_VERSION_BYTE] =
259 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
260 buf[OFF_SECTION_NUMBER] = self.section_number;
261 buf[OFF_LAST_SECTION_NUMBER] = self.last_section_number;
262 buf[OFF_PLATFORM_ID] = ((self.platform_id >> 16) & 0xFF) as u8;
264 buf[OFF_PLATFORM_ID + 1] = ((self.platform_id >> 8) & 0xFF) as u8;
265 buf[OFF_PLATFORM_ID + 2] = (self.platform_id & 0xFF) as u8;
266 buf[OFF_PROCESSING_ORDER] = self.processing_order;
267
268 let pdl = self.platform_descriptors.len() as u16;
270 buf[OFF_PLATFORM_DESC_LEN] = 0xF0 | ((pdl >> 8) as u8 & 0x0F);
271 buf[OFF_PLATFORM_DESC_LEN + 1] = (pdl & 0xFF) as u8;
272
273 let plat_start = OFF_PLATFORM_DESC_LEN + LOOP_LEN_FIELD;
275 let plat_end = plat_start + self.platform_descriptors.len();
276 buf[plat_start..plat_end].copy_from_slice(self.platform_descriptors.raw());
277
278 let loops_start = plat_end;
280 let loops_end = loops_start + self.loops.len();
281 buf[loops_start..loops_end].copy_from_slice(self.loops);
282
283 let crc_pos = len - CRC_LEN;
285 let crc = dvb_common::crc32_mpeg2::compute(&buf[..crc_pos]);
286 buf[crc_pos..len].copy_from_slice(&crc.to_be_bytes());
287
288 Ok(len)
289 }
290}
291
292impl<'a> Table<'a> for Int<'a> {
295 const TABLE_ID: u8 = TABLE_ID;
296 const PID: u16 = PID;
297}
298
299impl<'a> crate::traits::TableDef<'a> for Int<'a> {
300 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
301 const NAME: &'static str = "IP_MAC_NOTIFICATION";
302}
303
304#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[allow(clippy::too_many_arguments)]
316 fn build_int(
317 action_type: u8,
318 platform_id_hash: u8,
319 version_number: u8,
320 current_next_indicator: bool,
321 section_number: u8,
322 last_section_number: u8,
323 platform_id: u32,
324 processing_order: u8,
325 platform_desc: &[u8],
326 loops: &[u8],
327 ) -> Vec<u8> {
328 let int = Int {
329 action_type,
330 platform_id_hash,
331 version_number,
332 current_next_indicator,
333 section_number,
334 last_section_number,
335 platform_id,
336 processing_order,
337 platform_descriptors: DescriptorLoop::new(platform_desc),
338 loops,
339 };
340 let mut buf = vec![0u8; int.serialized_len()];
341 int.serialize_into(&mut buf).unwrap();
342 buf
343 }
344
345 #[test]
346 fn parse_happy_path() {
347 let bytes = build_int(
349 ACTION_TYPE_STREAM_ANNOUNCEMENT,
350 0x12 ^ 0x34, 3,
353 true,
354 0,
355 0,
356 0x00_12_34,
357 0x00,
358 &[0x81, 0x02, 0xAB, 0xCD],
359 &[],
360 );
361 let int = Int::parse(&bytes).unwrap();
362
363 assert_eq!(int.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
364 assert_eq!(int.platform_id_hash, 0x12 ^ 0x34);
365 assert_eq!(int.version_number, 3);
366 assert!(int.current_next_indicator);
367 assert_eq!(int.section_number, 0);
368 assert_eq!(int.last_section_number, 0);
369 assert_eq!(int.platform_id, 0x00_12_34);
370 assert_eq!(int.processing_order, 0x00);
371 assert_eq!(
372 int.platform_descriptors.raw(),
373 &[0x81, 0x02, 0xAB, 0xCD][..]
374 );
375 assert_eq!(int.loops, &[] as &[u8]);
376 }
377
378 #[test]
379 fn parse_happy_path_with_loops() {
380 let fake_loops: [u8; 4] = [
383 0xF0, 0x00, 0xF0, 0x00, ];
386 let bytes = build_int(
387 0x01,
388 0x56,
389 5,
390 false,
391 1,
392 1,
393 0x00_56_78,
394 0x01,
395 &[],
396 &fake_loops,
397 );
398 let int = Int::parse(&bytes).unwrap();
399 assert_eq!(int.platform_id, 0x00_56_78);
400 assert_eq!(int.version_number, 5);
401 assert!(!int.current_next_indicator);
402 assert_eq!(int.loops, &fake_loops[..]);
403 }
404
405 #[test]
406 fn parse_rejects_wrong_table_id() {
407 let mut bytes = build_int(0x01, 0x00, 0, true, 0, 0, 0x000001, 0x00, &[], &[]);
408 bytes[0] = 0x4B; let err = Int::parse(&bytes).unwrap_err();
410 assert!(matches!(
411 err,
412 Error::UnexpectedTableId { table_id: 0x4B, .. }
413 ));
414 }
415
416 #[test]
417 fn parse_rejects_buffer_too_short() {
418 let err = Int::parse(&[TABLE_ID, 0xF0]).unwrap_err();
419 assert!(matches!(err, Error::BufferTooShort { what: "Int", .. }));
420 }
421
422 #[test]
423 fn serialize_round_trip() {
424 let plat_desc = [0x7C, 0x04, 0x01, 0x02, 0x03, 0x04];
425 let fake_loops: [u8; 4] = [0xF0, 0x00, 0xF0, 0x00];
426 let bytes = build_int(
427 ACTION_TYPE_STREAM_ANNOUNCEMENT,
428 0xAB,
429 15,
430 true,
431 2,
432 3,
433 0x00_AB_CD,
434 0x00,
435 &plat_desc,
436 &fake_loops,
437 );
438
439 let int = Int::parse(&bytes).unwrap();
440
441 let mut buf = vec![0u8; int.serialized_len()];
443 int.serialize_into(&mut buf).unwrap();
444
445 let re = Int::parse(&buf).unwrap();
447
448 assert_eq!(int, re);
449 assert_eq!(re.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
450 assert_eq!(re.platform_id_hash, 0xAB);
451 assert_eq!(re.version_number, 15);
452 assert!(re.current_next_indicator);
453 assert_eq!(re.section_number, 2);
454 assert_eq!(re.last_section_number, 3);
455 assert_eq!(re.platform_id, 0x00_AB_CD);
456 assert_eq!(re.processing_order, 0x00);
457 assert_eq!(re.platform_descriptors.raw(), &plat_desc[..]);
458 assert_eq!(re.loops, &fake_loops[..]);
459 }
460
461 #[test]
462 fn serialize_rejects_too_small_output_buffer() {
463 let int = Int {
464 action_type: 0x01,
465 platform_id_hash: 0x00,
466 version_number: 0,
467 current_next_indicator: true,
468 section_number: 0,
469 last_section_number: 0,
470 platform_id: 0,
471 processing_order: 0,
472 platform_descriptors: DescriptorLoop::new(&[]),
473 loops: &[],
474 };
475 let mut buf = vec![0u8; 2]; let err = int.serialize_into(&mut buf).unwrap_err();
477 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
478 }
479
480 #[test]
481 fn platform_id_24bit_boundary() {
482 let bytes = build_int(0x01, 0xFF, 0, true, 0, 0, 0x00FF_FFFF, 0x00, &[], &[]);
484 let int = Int::parse(&bytes).unwrap();
485 assert_eq!(int.platform_id, 0x00FF_FFFF);
486 }
487}