1use super::descriptor_body;
27use crate::error::{Error, Result};
28use dvb_common::{Parse, Serialize};
29
30pub const TAG: u8 = 0x79;
32const HEADER_LEN: usize = 2;
33const FLAGS_LEN: usize = 1;
34const SCRAMBLING_FIELD_LEN: usize = 3;
35const ISI_FIELD_LEN: usize = 1;
36const TIMESLICE_FIELD_LEN: usize = 1;
37
38const FLAG_SCRAMBLING_SELECTOR: u8 = 0x80;
39const FLAG_MULTIPLE_INPUT_STREAM: u8 = 0x40;
40const FLAG_NOT_TIMESLICE: u8 = 0x10;
41const TS_GS_MODE_MASK: u8 = 0x03;
43const FLAG_RESERVED_BITS: u8 = 0x0C;
47
48const SCRAMBLING_RESERVED_MASK: u8 = 0xFC;
49const SCRAMBLING_INDEX_HI_MASK: u8 = 0x03;
50const SCRAMBLING_INDEX_MAX: u32 = 0x3FFFF;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55#[non_exhaustive]
56pub enum TsGsMode {
57 GenericPacketized,
59 Gse,
61 DvbTransportStream,
63 Reserved(u8),
65}
66
67impl TsGsMode {
68 #[must_use]
69 pub fn from_u8(v: u8) -> Self {
71 match v {
72 0 => TsGsMode::GenericPacketized,
73 1 => TsGsMode::Gse,
74 2 => TsGsMode::DvbTransportStream,
75 other => TsGsMode::Reserved(other),
76 }
77 }
78
79 #[must_use]
80 pub fn to_u8(self) -> u8 {
82 match self {
83 TsGsMode::GenericPacketized => 0,
84 TsGsMode::Gse => 1,
85 TsGsMode::DvbTransportStream => 2,
86 TsGsMode::Reserved(v) => v,
87 }
88 }
89
90 #[must_use]
91 pub fn name(self) -> &'static str {
93 match self {
94 TsGsMode::GenericPacketized => "Generic Packetized",
95 TsGsMode::Gse => "Generic Stream Encapsulation (GSE)",
96 TsGsMode::DvbTransportStream => "DVB transport stream",
97 TsGsMode::Reserved(_) => "reserved",
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize))]
105pub struct S2SatelliteDeliverySystemDescriptor {
106 pub scrambling_sequence_selector: bool,
108 pub multiple_input_stream_flag: bool,
110 pub not_timeslice_flag: bool,
112 pub ts_gs_mode: TsGsMode,
114 pub scrambling_sequence_index: Option<u32>,
116 pub input_stream_identifier: Option<u8>,
118 pub timeslice_number: Option<u8>,
120}
121
122impl<'a> Parse<'a> for S2SatelliteDeliverySystemDescriptor {
123 type Error = crate::error::Error;
124 fn parse(bytes: &'a [u8]) -> Result<Self> {
125 if bytes.len() < HEADER_LEN + FLAGS_LEN {
126 return Err(Error::BufferTooShort {
127 need: HEADER_LEN + FLAGS_LEN,
128 have: bytes.len(),
129 what: "S2SatelliteDeliverySystemDescriptor header",
130 });
131 }
132 let body = descriptor_body(
133 bytes,
134 TAG,
135 "S2SatelliteDeliverySystemDescriptor",
136 "unexpected tag for s2_satellite_delivery_system_descriptor",
137 )?;
138 if body.len() < FLAGS_LEN {
139 return Err(Error::InvalidDescriptor {
140 tag: TAG,
141 reason: "body must contain at least the flags byte",
142 });
143 }
144
145 let flags = body[0];
147 let scrambling_sequence_selector = (flags & FLAG_SCRAMBLING_SELECTOR) != 0;
148 let multiple_input_stream_flag = (flags & FLAG_MULTIPLE_INPUT_STREAM) != 0;
149 let not_timeslice_flag = (flags & FLAG_NOT_TIMESLICE) != 0;
150 let ts_gs_mode = TsGsMode::from_u8(flags & TS_GS_MODE_MASK);
151
152 let mut pos = FLAGS_LEN;
153
154 let scrambling_sequence_index = if scrambling_sequence_selector {
155 if pos + SCRAMBLING_FIELD_LEN > body.len() {
156 return Err(Error::BufferTooShort {
157 need: pos + SCRAMBLING_FIELD_LEN,
158 have: body.len(),
159 what: "S2SatelliteDeliverySystemDescriptor scrambling_sequence_index",
160 });
161 }
162 let b0 = body[pos];
163 let index = (u32::from(b0 & SCRAMBLING_INDEX_HI_MASK) << 16)
164 | (u32::from(body[pos + 1]) << 8)
165 | u32::from(body[pos + 2]);
166 pos += SCRAMBLING_FIELD_LEN;
167 Some(index)
168 } else {
169 None
170 };
171
172 let input_stream_identifier = if multiple_input_stream_flag {
173 if pos + ISI_FIELD_LEN > body.len() {
174 return Err(Error::BufferTooShort {
175 need: pos + ISI_FIELD_LEN,
176 have: body.len(),
177 what: "S2SatelliteDeliverySystemDescriptor input_stream_identifier",
178 });
179 }
180 let isi = body[pos];
181 pos += ISI_FIELD_LEN;
182 Some(isi)
183 } else {
184 None
185 };
186
187 let timeslice_number = if !not_timeslice_flag {
189 if pos + TIMESLICE_FIELD_LEN > body.len() {
190 return Err(Error::BufferTooShort {
191 need: pos + TIMESLICE_FIELD_LEN,
192 have: body.len(),
193 what: "S2SatelliteDeliverySystemDescriptor timeslice_number",
194 });
195 }
196 Some(body[pos])
197 } else {
198 None
199 };
200
201 Ok(Self {
202 scrambling_sequence_selector,
203 multiple_input_stream_flag,
204 not_timeslice_flag,
205 ts_gs_mode,
206 scrambling_sequence_index,
207 input_stream_identifier,
208 timeslice_number,
209 })
210 }
211}
212
213impl Serialize for S2SatelliteDeliverySystemDescriptor {
214 type Error = crate::error::Error;
215 fn serialized_len(&self) -> usize {
216 HEADER_LEN
217 + FLAGS_LEN
218 + if self.scrambling_sequence_selector {
219 SCRAMBLING_FIELD_LEN
220 } else {
221 0
222 }
223 + if self.multiple_input_stream_flag {
224 ISI_FIELD_LEN
225 } else {
226 0
227 }
228 + if self.not_timeslice_flag {
229 0
230 } else {
231 TIMESLICE_FIELD_LEN
232 }
233 }
234
235 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
236 let len = self.serialized_len();
237 if buf.len() < len {
238 return Err(Error::OutputBufferTooSmall {
239 need: len,
240 have: buf.len(),
241 });
242 }
243 buf[0] = TAG;
244 buf[1] = (len - HEADER_LEN) as u8;
245
246 let mut flags = FLAG_RESERVED_BITS | (self.ts_gs_mode.to_u8() & TS_GS_MODE_MASK);
247 if self.scrambling_sequence_selector {
248 flags |= FLAG_SCRAMBLING_SELECTOR;
249 }
250 if self.multiple_input_stream_flag {
251 flags |= FLAG_MULTIPLE_INPUT_STREAM;
252 }
253 if self.not_timeslice_flag {
254 flags |= FLAG_NOT_TIMESLICE;
255 }
256 buf[HEADER_LEN] = flags;
257
258 let mut pos = HEADER_LEN + FLAGS_LEN;
259 if self.scrambling_sequence_selector {
260 let index = self.scrambling_sequence_index.unwrap_or(0) & SCRAMBLING_INDEX_MAX;
261 buf[pos] = SCRAMBLING_RESERVED_MASK | ((index >> 16) as u8 & SCRAMBLING_INDEX_HI_MASK);
262 buf[pos + 1] = (index >> 8) as u8;
263 buf[pos + 2] = index as u8;
264 pos += SCRAMBLING_FIELD_LEN;
265 }
266 if self.multiple_input_stream_flag {
267 buf[pos] = self.input_stream_identifier.unwrap_or(0);
268 pos += ISI_FIELD_LEN;
269 }
270 if !self.not_timeslice_flag {
271 buf[pos] = self.timeslice_number.unwrap_or(0);
272 }
273 Ok(len)
274 }
275}
276impl<'a> crate::traits::DescriptorDef<'a> for S2SatelliteDeliverySystemDescriptor {
277 const TAG: u8 = TAG;
278 const NAME: &'static str = "S2_SATELLITE_DELIVERY_SYSTEM";
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn ts_gs_mode_from_u8_to_u8_roundtrip() {
287 for b in 0..=0xFFu8 {
288 assert_eq!(
289 TsGsMode::from_u8(b).to_u8(),
290 b,
291 "round-trip fail for {b:#04x}"
292 );
293 }
294 }
295
296 #[test]
299 fn parse_minimal_with_timeslice() {
300 let raw = [TAG, 2, 0x2C, 0x07];
301 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
302 assert!(!d.scrambling_sequence_selector);
303 assert!(!d.multiple_input_stream_flag);
304 assert!(!d.not_timeslice_flag);
305 assert_eq!(d.ts_gs_mode, TsGsMode::GenericPacketized);
306 assert_eq!(d.timeslice_number, Some(0x07));
307 assert_eq!(d.scrambling_sequence_index, None);
308 assert_eq!(d.input_stream_identifier, None);
309 }
310
311 #[test]
312 fn parse_not_timeslice_omits_timeslice_number() {
313 let raw = [TAG, 1, 0x3E];
315 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
316 assert!(d.not_timeslice_flag);
317 assert_eq!(d.ts_gs_mode, TsGsMode::DvbTransportStream);
318 assert_eq!(d.timeslice_number, None);
319 }
320
321 #[test]
322 fn parse_extracts_isi_and_timeslice() {
323 let raw = [TAG, 3, 0x6C, 0x05, 0x09];
325 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
326 assert!(d.multiple_input_stream_flag);
327 assert_eq!(d.input_stream_identifier, Some(5));
328 assert_eq!(d.timeslice_number, Some(9));
329 }
330
331 #[test]
332 fn parse_extracts_scrambling_index() {
333 let raw = [TAG, 4, 0xBC, 0xFD, 0x23, 0x45];
335 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
336 assert!(d.scrambling_sequence_selector);
337 assert_eq!(d.scrambling_sequence_index, Some(0x12345));
338 assert!(d.not_timeslice_flag);
339 assert_eq!(d.timeslice_number, None);
340 }
341
342 #[test]
343 fn parse_full_18_bit_index() {
344 let raw = [TAG, 4, 0xBC, 0xFF, 0xFF, 0xFF];
345 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
346 assert_eq!(d.scrambling_sequence_index, Some(0x3FFFF));
347 }
348
349 #[test]
350 fn parse_rejects_wrong_tag() {
351 let raw = [0x44, 1, 0x3E];
352 assert!(matches!(
353 S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap_err(),
354 Error::InvalidDescriptor { tag: 0x44, .. }
355 ));
356 }
357
358 #[test]
359 fn parse_rejects_truncated_scrambling() {
360 let raw = [TAG, 1, 0x9C]; assert!(matches!(
362 S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap_err(),
363 Error::BufferTooShort { .. }
364 ));
365 }
366
367 #[test]
368 fn serialize_round_trip_all_fields() {
369 let d = S2SatelliteDeliverySystemDescriptor {
370 scrambling_sequence_selector: true,
371 multiple_input_stream_flag: true,
372 not_timeslice_flag: false,
373 ts_gs_mode: TsGsMode::DvbTransportStream,
374 scrambling_sequence_index: Some(0x2BCDE),
375 input_stream_identifier: Some(0x42),
376 timeslice_number: Some(0x11),
377 };
378 let mut buf = vec![0u8; d.serialized_len()];
379 d.serialize_into(&mut buf).unwrap();
380 assert_eq!(S2SatelliteDeliverySystemDescriptor::parse(&buf).unwrap(), d);
381 }
382
383 #[test]
384 fn serialize_round_trip_not_timeslice() {
385 let d = S2SatelliteDeliverySystemDescriptor {
386 scrambling_sequence_selector: false,
387 multiple_input_stream_flag: false,
388 not_timeslice_flag: true,
389 ts_gs_mode: TsGsMode::Gse,
390 scrambling_sequence_index: None,
391 input_stream_identifier: None,
392 timeslice_number: None,
393 };
394 let mut buf = vec![0u8; d.serialized_len()];
395 d.serialize_into(&mut buf).unwrap();
396 assert_eq!(S2SatelliteDeliverySystemDescriptor::parse(&buf).unwrap(), d);
397 }
398}