1use crate::error::{Error, Result};
24use crate::traits::Table;
25use dvb_common::{Parse, Serialize};
26
27pub const TABLE_ID: u8 = 0x4C;
29
30pub const PID: u16 = 0x0000;
37
38pub const ACTION_TYPE_STREAM_ANNOUNCEMENT: u8 = 0x01;
40
41const OUTER_HEADER_LEN: usize = 3;
45
46const INT_FIXED_LEN: usize = 9;
51
52const LOOP_LEN_FIELD: usize = 2;
54
55const CRC_LEN: usize = 4;
57
58const MIN_SECTION_LEN: usize = OUTER_HEADER_LEN + INT_FIXED_LEN + LOOP_LEN_FIELD + CRC_LEN;
61
62const OFF_ACTION_TYPE: usize = 3;
66const OFF_PLATFORM_ID_HASH: usize = 4;
68const OFF_VERSION_BYTE: usize = 5;
70const OFF_SECTION_NUMBER: usize = 6;
72const OFF_LAST_SECTION_NUMBER: usize = 7;
74const OFF_PLATFORM_ID: usize = 8;
76const OFF_PROCESSING_ORDER: usize = 11;
78const OFF_PLATFORM_DESC_LEN: usize = 12;
80
81#[derive(Debug, Clone, PartialEq, Eq)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93pub struct Int<'a> {
94 pub action_type: u8,
96
97 pub platform_id_hash: u8,
101
102 pub version_number: u8,
104
105 pub current_next_indicator: bool,
108
109 pub section_number: u8,
111
112 pub last_section_number: u8,
114
115 pub platform_id: u32,
118
119 pub processing_order: u8,
122
123 #[cfg_attr(feature = "serde", serde(borrow))]
126 pub platform_descriptors: &'a [u8],
127
128 #[cfg_attr(feature = "serde", serde(borrow))]
137 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 = &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);
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
299#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[allow(clippy::too_many_arguments)]
311 fn build_int(
312 action_type: u8,
313 platform_id_hash: u8,
314 version_number: u8,
315 current_next_indicator: bool,
316 section_number: u8,
317 last_section_number: u8,
318 platform_id: u32,
319 processing_order: u8,
320 platform_desc: &[u8],
321 loops: &[u8],
322 ) -> Vec<u8> {
323 let int = Int {
324 action_type,
325 platform_id_hash,
326 version_number,
327 current_next_indicator,
328 section_number,
329 last_section_number,
330 platform_id,
331 processing_order,
332 platform_descriptors: platform_desc,
333 loops,
334 };
335 let mut buf = vec![0u8; int.serialized_len()];
336 int.serialize_into(&mut buf).unwrap();
337 buf
338 }
339
340 #[test]
341 fn parse_happy_path() {
342 let bytes = build_int(
344 ACTION_TYPE_STREAM_ANNOUNCEMENT,
345 0x12 ^ 0x34, 3,
348 true,
349 0,
350 0,
351 0x00_12_34,
352 0x00,
353 &[0x81, 0x02, 0xAB, 0xCD],
354 &[],
355 );
356 let int = Int::parse(&bytes).unwrap();
357
358 assert_eq!(int.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
359 assert_eq!(int.platform_id_hash, 0x12 ^ 0x34);
360 assert_eq!(int.version_number, 3);
361 assert!(int.current_next_indicator);
362 assert_eq!(int.section_number, 0);
363 assert_eq!(int.last_section_number, 0);
364 assert_eq!(int.platform_id, 0x00_12_34);
365 assert_eq!(int.processing_order, 0x00);
366 assert_eq!(int.platform_descriptors, &[0x81, 0x02, 0xAB, 0xCD][..]);
367 assert_eq!(int.loops, &[] as &[u8]);
368 }
369
370 #[test]
371 fn parse_happy_path_with_loops() {
372 let fake_loops: [u8; 4] = [
375 0xF0, 0x00, 0xF0, 0x00, ];
378 let bytes = build_int(
379 0x01,
380 0x56,
381 5,
382 false,
383 1,
384 1,
385 0x00_56_78,
386 0x01,
387 &[],
388 &fake_loops,
389 );
390 let int = Int::parse(&bytes).unwrap();
391 assert_eq!(int.platform_id, 0x00_56_78);
392 assert_eq!(int.version_number, 5);
393 assert!(!int.current_next_indicator);
394 assert_eq!(int.loops, &fake_loops[..]);
395 }
396
397 #[test]
398 fn parse_rejects_wrong_table_id() {
399 let mut bytes = build_int(0x01, 0x00, 0, true, 0, 0, 0x000001, 0x00, &[], &[]);
400 bytes[0] = 0x4B; let err = Int::parse(&bytes).unwrap_err();
402 assert!(matches!(
403 err,
404 Error::UnexpectedTableId { table_id: 0x4B, .. }
405 ));
406 }
407
408 #[test]
409 fn parse_rejects_buffer_too_short() {
410 let err = Int::parse(&[TABLE_ID, 0xF0]).unwrap_err();
411 assert!(matches!(err, Error::BufferTooShort { what: "Int", .. }));
412 }
413
414 #[test]
415 fn serialize_round_trip() {
416 let plat_desc = [0x7C, 0x04, 0x01, 0x02, 0x03, 0x04];
417 let fake_loops: [u8; 4] = [0xF0, 0x00, 0xF0, 0x00];
418 let bytes = build_int(
419 ACTION_TYPE_STREAM_ANNOUNCEMENT,
420 0xAB,
421 15,
422 true,
423 2,
424 3,
425 0x00_AB_CD,
426 0x00,
427 &plat_desc,
428 &fake_loops,
429 );
430
431 let int = Int::parse(&bytes).unwrap();
432
433 let mut buf = vec![0u8; int.serialized_len()];
435 int.serialize_into(&mut buf).unwrap();
436
437 let re = Int::parse(&buf).unwrap();
439
440 assert_eq!(int, re);
441 assert_eq!(re.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
442 assert_eq!(re.platform_id_hash, 0xAB);
443 assert_eq!(re.version_number, 15);
444 assert!(re.current_next_indicator);
445 assert_eq!(re.section_number, 2);
446 assert_eq!(re.last_section_number, 3);
447 assert_eq!(re.platform_id, 0x00_AB_CD);
448 assert_eq!(re.processing_order, 0x00);
449 assert_eq!(re.platform_descriptors, &plat_desc[..]);
450 assert_eq!(re.loops, &fake_loops[..]);
451 }
452
453 #[test]
454 fn serialize_rejects_too_small_output_buffer() {
455 let int = Int {
456 action_type: 0x01,
457 platform_id_hash: 0x00,
458 version_number: 0,
459 current_next_indicator: true,
460 section_number: 0,
461 last_section_number: 0,
462 platform_id: 0,
463 processing_order: 0,
464 platform_descriptors: &[],
465 loops: &[],
466 };
467 let mut buf = vec![0u8; 2]; let err = int.serialize_into(&mut buf).unwrap_err();
469 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
470 }
471
472 #[test]
473 fn platform_id_24bit_boundary() {
474 let bytes = build_int(0x01, 0xFF, 0, true, 0, 0, 0x00FF_FFFF, 0x00, &[], &[]);
476 let int = Int::parse(&bytes).unwrap();
477 assert_eq!(int.platform_id, 0x00FF_FFFF);
478 }
479}