Skip to main content

dvb_si/descriptors/
partial_transport_stream.rs

1//! Partial Transport Stream Descriptor — ETSI EN 300 468 §7.2.1 (tag 0x63).
2//!
3//! Table 165 (PDF p. 155). Carried in the SIT (Selection Information Table) of a
4//! partial TS to describe its bit-rate / smoothing-buffer characteristics. The
5//! body is a FIXED 8-byte structure (NOT a loop): three reserved-prefixed
6//! fields. The published markdown rendering of Table 165 is faithful to the PDF
7//! (verified against PDF p. 155, §7.2.1):
8//!
9//! - reserved_future_use (2) + peak_rate (22)
10//! - reserved_future_use (2) + minimum_overall_smoothing_rate (22)
11//! - reserved_future_use (2) + maximum_overall_smoothing_buffer (14)
12//!
13//! Reserved bits are ignored on parse and emitted as 1s on serialize (matching
14//! the `reserved_future_use` convention used elsewhere in this crate).
15
16use super::descriptor_body;
17use crate::error::{Error, Result};
18use dvb_common::{Parse, Serialize};
19
20/// Descriptor tag for partial_transport_stream_descriptor.
21pub const TAG: u8 = 0x63;
22const HEADER_LEN: usize = 2;
23/// Fixed payload length: 3×24-bit reserved-prefixed words (EN 300 468 Table 165).
24const BODY_LEN: u8 = 8;
25/// Maximum value for a 22-bit field (peak_rate / minimum_overall_smoothing_rate).
26const MAX_22: u32 = (1 << 22) - 1;
27/// Maximum value for the 14-bit maximum_overall_smoothing_buffer field.
28const MAX_14: u16 = (1 << 14) - 1;
29
30/// Partial Transport Stream Descriptor (tag 0x63).
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct PartialTransportStreamDescriptor {
34    /// 22-bit peak_rate (188-byte packets per unit interval, PDF p. 155).
35    pub peak_rate: u32,
36    /// 22-bit minimum_overall_smoothing_rate (0x3FFFFF = undefined, PDF p. 155).
37    pub minimum_overall_smoothing_rate: u32,
38    /// 14-bit maximum_overall_smoothing_buffer (0x3FFF = undefined, PDF p. 155).
39    pub maximum_overall_smoothing_buffer: u16,
40}
41
42impl<'a> Parse<'a> for PartialTransportStreamDescriptor {
43    type Error = crate::error::Error;
44    fn parse(bytes: &'a [u8]) -> Result<Self> {
45        let body = descriptor_body(
46            bytes,
47            TAG,
48            "PartialTransportStreamDescriptor",
49            "unexpected tag for partial_transport_stream_descriptor",
50        )?;
51        if body.len() != BODY_LEN as usize {
52            return Err(Error::InvalidDescriptor {
53                tag: TAG,
54                reason: "partial_transport_stream_descriptor length must equal 8",
55            });
56        }
57        let peak_rate =
58            (u32::from(body[0] & 0x3F) << 16) | (u32::from(body[1]) << 8) | u32::from(body[2]);
59        let minimum_overall_smoothing_rate =
60            (u32::from(body[3] & 0x3F) << 16) | (u32::from(body[4]) << 8) | u32::from(body[5]);
61        let maximum_overall_smoothing_buffer =
62            (u16::from(body[6] & 0x3F) << 8) | u16::from(body[7]);
63        Ok(Self {
64            peak_rate,
65            minimum_overall_smoothing_rate,
66            maximum_overall_smoothing_buffer,
67        })
68    }
69}
70
71impl Serialize for PartialTransportStreamDescriptor {
72    type Error = crate::error::Error;
73    fn serialized_len(&self) -> usize {
74        HEADER_LEN + BODY_LEN as usize
75    }
76
77    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
78        if self.peak_rate > MAX_22 || self.minimum_overall_smoothing_rate > MAX_22 {
79            return Err(Error::InvalidDescriptor {
80                tag: TAG,
81                reason: "peak_rate / minimum_overall_smoothing_rate exceed 22 bits",
82            });
83        }
84        if self.maximum_overall_smoothing_buffer > MAX_14 {
85            return Err(Error::InvalidDescriptor {
86                tag: TAG,
87                reason: "maximum_overall_smoothing_buffer exceeds 14 bits",
88            });
89        }
90        let len = self.serialized_len();
91        if buf.len() < len {
92            return Err(Error::OutputBufferTooSmall {
93                need: len,
94                have: buf.len(),
95            });
96        }
97        buf[0] = TAG;
98        buf[1] = BODY_LEN;
99        let b = HEADER_LEN;
100        // reserved_future_use bits emitted as 1s (0xC0 mask on the high byte).
101        buf[b] = 0xC0 | ((self.peak_rate >> 16) as u8 & 0x3F);
102        buf[b + 1] = (self.peak_rate >> 8) as u8;
103        buf[b + 2] = self.peak_rate as u8;
104        buf[b + 3] = 0xC0 | ((self.minimum_overall_smoothing_rate >> 16) as u8 & 0x3F);
105        buf[b + 4] = (self.minimum_overall_smoothing_rate >> 8) as u8;
106        buf[b + 5] = self.minimum_overall_smoothing_rate as u8;
107        buf[b + 6] = 0xC0 | ((self.maximum_overall_smoothing_buffer >> 8) as u8 & 0x3F);
108        buf[b + 7] = self.maximum_overall_smoothing_buffer as u8;
109        Ok(len)
110    }
111}
112impl<'a> crate::traits::DescriptorDef<'a> for PartialTransportStreamDescriptor {
113    const TAG: u8 = TAG;
114    const NAME: &'static str = "PARTIAL_TRANSPORT_STREAM";
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn parse_extracts_fields_and_ignores_reserved() {
123        // peak_rate=0x012345, min=0x0ABCDE, max=0x1234. Reserved bits set to 1.
124        let bytes = [TAG, 8, 0xC1, 0x23, 0x45, 0xCA, 0xBC, 0xDE, 0xD2, 0x34];
125        let d = PartialTransportStreamDescriptor::parse(&bytes).unwrap();
126        assert_eq!(d.peak_rate, 0x01_2345);
127        assert_eq!(d.minimum_overall_smoothing_rate, 0x0A_BCDE);
128        assert_eq!(d.maximum_overall_smoothing_buffer, 0x1234);
129    }
130
131    #[test]
132    fn parse_rejects_wrong_tag() {
133        let err = PartialTransportStreamDescriptor::parse(&[0x64, 8, 0, 0, 0, 0, 0, 0, 0, 0])
134            .unwrap_err();
135        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x64, .. }));
136    }
137
138    #[test]
139    fn parse_rejects_short_buffer() {
140        let err = PartialTransportStreamDescriptor::parse(&[TAG]).unwrap_err();
141        assert!(matches!(err, Error::BufferTooShort { .. }));
142    }
143
144    #[test]
145    fn parse_rejects_truncated_body() {
146        // length=8 but only 4 payload bytes present.
147        let err = PartialTransportStreamDescriptor::parse(&[TAG, 8, 0, 0, 0, 0]).unwrap_err();
148        assert!(matches!(err, Error::BufferTooShort { .. }));
149    }
150
151    #[test]
152    fn parse_rejects_wrong_length() {
153        let err =
154            PartialTransportStreamDescriptor::parse(&[TAG, 7, 0, 0, 0, 0, 0, 0, 0]).unwrap_err();
155        assert!(matches!(err, Error::InvalidDescriptor { .. }));
156    }
157
158    #[test]
159    fn serialize_round_trip() {
160        let d = PartialTransportStreamDescriptor {
161            peak_rate: 0x0F_FFFF,
162            minimum_overall_smoothing_rate: 0x00_0001,
163            maximum_overall_smoothing_buffer: 0x2ABC,
164        };
165        let mut buf = vec![0u8; d.serialized_len()];
166        d.serialize_into(&mut buf).unwrap();
167        let re = PartialTransportStreamDescriptor::parse(&buf).unwrap();
168        assert_eq!(d, re);
169    }
170
171    #[test]
172    fn serialize_emits_reserved_ones() {
173        let d = PartialTransportStreamDescriptor {
174            peak_rate: 0,
175            minimum_overall_smoothing_rate: 0,
176            maximum_overall_smoothing_buffer: 0,
177        };
178        let mut buf = vec![0u8; d.serialized_len()];
179        d.serialize_into(&mut buf).unwrap();
180        // The three reserved-prefixed bytes have their top two bits set.
181        assert_eq!(buf[2] & 0xC0, 0xC0);
182        assert_eq!(buf[5] & 0xC0, 0xC0);
183        assert_eq!(buf[8] & 0xC0, 0xC0);
184    }
185
186    #[test]
187    fn serialize_rejects_too_small_buffer() {
188        let d = PartialTransportStreamDescriptor {
189            peak_rate: 1,
190            minimum_overall_smoothing_rate: 1,
191            maximum_overall_smoothing_buffer: 1,
192        };
193        let mut tiny = [0u8; 5];
194        let err = d.serialize_into(&mut tiny).unwrap_err();
195        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
196    }
197
198    #[test]
199    fn serialize_rejects_over_range_22bit() {
200        let d = PartialTransportStreamDescriptor {
201            peak_rate: 1 << 22, // one past 22-bit max
202            minimum_overall_smoothing_rate: 0,
203            maximum_overall_smoothing_buffer: 0,
204        };
205        let mut buf = vec![0u8; d.serialized_len()];
206        let err = d.serialize_into(&mut buf).unwrap_err();
207        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
208    }
209
210    #[test]
211    fn serialize_rejects_over_range_14bit() {
212        let d = PartialTransportStreamDescriptor {
213            peak_rate: 0,
214            minimum_overall_smoothing_rate: 0,
215            maximum_overall_smoothing_buffer: 1 << 14, // one past 14-bit max
216        };
217        let mut buf = vec![0u8; d.serialized_len()];
218        let err = d.serialize_into(&mut buf).unwrap_err();
219        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
220    }
221
222    #[test]
223    fn descriptor_length_matches_payload() {
224        let d = PartialTransportStreamDescriptor {
225            peak_rate: 0,
226            minimum_overall_smoothing_rate: 0,
227            maximum_overall_smoothing_buffer: 0,
228        };
229        assert_eq!(d.serialized_len() - 2, 8);
230    }
231
232    #[cfg(feature = "serde")]
233    #[test]
234    fn serde_round_trip() {
235        let d = PartialTransportStreamDescriptor {
236            peak_rate: 0x00_1234,
237            minimum_overall_smoothing_rate: 0x00_5678,
238            maximum_overall_smoothing_buffer: 0x09AB,
239        };
240        let json = serde_json::to_string(&d).unwrap();
241        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
242        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
243    }
244}