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,
347 true,
348 0,
349 0,
350 0x00_12_34,
351 0x00,
352 &[0x81, 0x02, 0xAB, 0xCD],
353 &[],
354 );
355 let int = Int::parse(&bytes).unwrap();
356
357 assert_eq!(int.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
358 assert_eq!(int.platform_id_hash, 0x12 ^ 0x34);
359 assert_eq!(int.version_number, 3);
360 assert!(int.current_next_indicator);
361 assert_eq!(int.section_number, 0);
362 assert_eq!(int.last_section_number, 0);
363 assert_eq!(int.platform_id, 0x00_12_34);
364 assert_eq!(int.processing_order, 0x00);
365 assert_eq!(int.platform_descriptors, &[0x81, 0x02, 0xAB, 0xCD][..]);
366 assert_eq!(int.loops, &[] as &[u8]);
367 }
368
369 #[test]
370 fn parse_happy_path_with_loops() {
371 let fake_loops: [u8; 4] = [
374 0xF0, 0x00, 0xF0, 0x00, ];
377 let bytes = build_int(
378 0x01, 0x56, 5, false, 1, 1, 0x00_56_78, 0x01, &[], &fake_loops,
379 );
380 let int = Int::parse(&bytes).unwrap();
381 assert_eq!(int.platform_id, 0x00_56_78);
382 assert_eq!(int.version_number, 5);
383 assert!(!int.current_next_indicator);
384 assert_eq!(int.loops, &fake_loops[..]);
385 }
386
387 #[test]
388 fn parse_rejects_wrong_table_id() {
389 let mut bytes = build_int(0x01, 0x00, 0, true, 0, 0, 0x000001, 0x00, &[], &[]);
390 bytes[0] = 0x4B; let err = Int::parse(&bytes).unwrap_err();
392 assert!(matches!(
393 err,
394 Error::UnexpectedTableId {
395 table_id: 0x4B,
396 ..
397 }
398 ));
399 }
400
401 #[test]
402 fn parse_rejects_buffer_too_short() {
403 let err = Int::parse(&[TABLE_ID, 0xF0]).unwrap_err();
404 assert!(matches!(err, Error::BufferTooShort { what: "Int", .. }));
405 }
406
407 #[test]
408 fn serialize_round_trip() {
409 let plat_desc = [0x7C, 0x04, 0x01, 0x02, 0x03, 0x04];
410 let fake_loops: [u8; 4] = [0xF0, 0x00, 0xF0, 0x00];
411 let bytes = build_int(
412 ACTION_TYPE_STREAM_ANNOUNCEMENT,
413 0xAB,
414 15,
415 true,
416 2,
417 3,
418 0x00_AB_CD,
419 0x00,
420 &plat_desc,
421 &fake_loops,
422 );
423
424 let int = Int::parse(&bytes).unwrap();
425
426 let mut buf = vec![0u8; int.serialized_len()];
428 int.serialize_into(&mut buf).unwrap();
429
430 let re = Int::parse(&buf).unwrap();
432
433 assert_eq!(int, re);
434 assert_eq!(re.action_type, ACTION_TYPE_STREAM_ANNOUNCEMENT);
435 assert_eq!(re.platform_id_hash, 0xAB);
436 assert_eq!(re.version_number, 15);
437 assert!(re.current_next_indicator);
438 assert_eq!(re.section_number, 2);
439 assert_eq!(re.last_section_number, 3);
440 assert_eq!(re.platform_id, 0x00_AB_CD);
441 assert_eq!(re.processing_order, 0x00);
442 assert_eq!(re.platform_descriptors, &plat_desc[..]);
443 assert_eq!(re.loops, &fake_loops[..]);
444 }
445
446 #[test]
447 fn serialize_rejects_too_small_output_buffer() {
448 let int = Int {
449 action_type: 0x01,
450 platform_id_hash: 0x00,
451 version_number: 0,
452 current_next_indicator: true,
453 section_number: 0,
454 last_section_number: 0,
455 platform_id: 0,
456 processing_order: 0,
457 platform_descriptors: &[],
458 loops: &[],
459 };
460 let mut buf = vec![0u8; 2]; let err = int.serialize_into(&mut buf).unwrap_err();
462 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
463 }
464
465 #[test]
466 fn platform_id_24bit_boundary() {
467 let bytes = build_int(0x01, 0xFF, 0, true, 0, 0, 0x00FF_FFFF, 0x00, &[], &[]);
469 let int = Int::parse(&bytes).unwrap();
470 assert_eq!(int.platform_id, 0x00FF_FFFF);
471 }
472}