Skip to main content

dvb_subtitle/segments/
end_of_display_set.rs

1//! End of Display Set Segment — ETSI EN 300 743 §7.2.6, Table 28 (segment_type 0x80).
2
3use crate::error::{Error, Result};
4use broadcast_common::len::{need_in, need_out};
5use broadcast_common::{Parse, Serialize};
6
7/// The end_of_display_set_segment segment_type.
8pub const SEGMENT_TYPE: u8 = 0x80;
9/// Header: sync_byte(1) + segment_type(1) + page_id(2) + segment_length(2) = 6 bytes. segment_length must be 0.
10pub const HEADER_LEN: usize = 6;
11
12/// End of Display Set Segment.
13#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize))]
15pub struct EndOfDisplaySetSegment {
16    /// The page_id (from generic segment header).
17    pub page_id: u16,
18}
19
20impl<'a> Parse<'a> for EndOfDisplaySetSegment {
21    type Error = Error;
22
23    fn parse(bytes: &'a [u8]) -> Result<Self> {
24        need_in(bytes, HEADER_LEN, "end_of_display_set_segment")?;
25        if bytes[0] != crate::pes_data_field::SYNC_BYTE {
26            return Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
27                what: "sync_byte",
28                expected: u32::from(crate::pes_data_field::SYNC_BYTE),
29                found: u32::from(bytes[0]),
30            }));
31        }
32        if bytes[1] != SEGMENT_TYPE {
33            return Err(Error::UnknownSegmentType(bytes[1]));
34        }
35        let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
36        let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]);
37        if segment_length != 0 {
38            return Err(Error::SegmentTooLarge);
39        }
40        Ok(EndOfDisplaySetSegment { page_id })
41    }
42}
43
44impl Serialize for EndOfDisplaySetSegment {
45    type Error = Error;
46
47    fn serialized_len(&self) -> usize {
48        HEADER_LEN
49    }
50
51    fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
52        let len = self.serialized_len();
53        need_out(buf, len)?;
54        buf[0] = 0x0F;
55        buf[1] = SEGMENT_TYPE;
56        buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
57        buf[4..6].copy_from_slice(&0u16.to_be_bytes()); // segment_length = 0
58        Ok(len)
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use broadcast_common::{Parse, Serialize};
66
67    #[test]
68    fn serialize_into_short_output_is_output_buffer_too_small() {
69        let bytes = [0x0F, 0x80, 0x00, 0x01, 0x00, 0x00];
70        let seg = EndOfDisplaySetSegment::parse(&bytes).unwrap();
71        let mut out = [0u8; 5];
72        assert_eq!(
73            seg.serialize_into(&mut out),
74            Err(crate::Error::OutputBufferTooSmall(
75                broadcast_common::error::OutputBufferTooSmall { need: 6, have: 5 }
76            ))
77        );
78    }
79
80    #[test]
81    fn round_trip() {
82        let bytes = [0x0F, 0x80, 0x00, 0x01, 0x00, 0x00];
83        let seg = EndOfDisplaySetSegment::parse(&bytes).unwrap();
84        assert_eq!(seg.page_id, 1);
85        let out = seg.try_to_bytes().unwrap();
86        assert_eq!(out, bytes);
87
88        // Biting test: mutation changes output
89        let mut seg2 = seg.clone();
90        seg2.page_id = 5;
91        let out2 = seg2.try_to_bytes().unwrap();
92        assert_ne!(out2, bytes);
93        let reparse = EndOfDisplaySetSegment::parse(&out2).unwrap();
94        assert_eq!(reparse.page_id, 5);
95    }
96
97    /// DS-W2 (#1108): sync_byte wasn't checked by any typed segment parser
98    /// (only by `PesDataField`, whose loop only ever calls a typed parser
99    /// with `bytes[0] == 0x0F` anyway), so a direct caller of `Self::parse`
100    /// could parse a non-segment, and re-serializing it would silently
101    /// change byte 0 back to `0x0F`.
102    #[test]
103    fn rejects_bad_sync_byte() {
104        let bytes = [0x00, 0x80, 0x00, 0x01, 0x00, 0x00];
105        assert!(matches!(
106            EndOfDisplaySetSegment::parse(&bytes),
107            Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
108                found: 0x00,
109                ..
110            }))
111        ));
112    }
113}