1use crate::error::{Error, Result};
27use crate::traits::Descriptor;
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;
44const FLAG_RESERVED_BITS: u8 = 0x0C;
48
49const SCRAMBLING_RESERVED_MASK: u8 = 0xFC;
50const SCRAMBLING_INDEX_HI_MASK: u8 = 0x03;
51const SCRAMBLING_INDEX_MAX: u32 = 0x3FFFF;
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub struct S2SatelliteDeliverySystemDescriptor {
57 pub scrambling_sequence_selector: bool,
59 pub multiple_input_stream_flag: bool,
61 pub not_timeslice_flag: bool,
63 pub ts_gs_mode: u8,
65 pub scrambling_sequence_index: Option<u32>,
67 pub input_stream_identifier: Option<u8>,
69 pub timeslice_number: Option<u8>,
71}
72
73impl<'a> Parse<'a> for S2SatelliteDeliverySystemDescriptor {
74 type Error = crate::error::Error;
75 fn parse(bytes: &'a [u8]) -> Result<Self> {
76 if bytes.len() < HEADER_LEN + FLAGS_LEN {
77 return Err(Error::BufferTooShort {
78 need: HEADER_LEN + FLAGS_LEN,
79 have: bytes.len(),
80 what: "S2SatelliteDeliverySystemDescriptor header",
81 });
82 }
83 if bytes[0] != TAG {
84 return Err(Error::InvalidDescriptor {
85 tag: bytes[0],
86 reason: "unexpected tag for s2_satellite_delivery_system_descriptor",
87 });
88 }
89 let length = bytes[1] as usize;
90 let end = HEADER_LEN + length;
91 if bytes.len() < end {
92 return Err(Error::BufferTooShort {
93 need: end,
94 have: bytes.len(),
95 what: "S2SatelliteDeliverySystemDescriptor body",
96 });
97 }
98 if length < FLAGS_LEN {
99 return Err(Error::InvalidDescriptor {
100 tag: TAG,
101 reason: "body must contain at least the flags byte",
102 });
103 }
104
105 let flags = bytes[HEADER_LEN];
107 let scrambling_sequence_selector = (flags & FLAG_SCRAMBLING_SELECTOR) != 0;
108 let multiple_input_stream_flag = (flags & FLAG_MULTIPLE_INPUT_STREAM) != 0;
109 let not_timeslice_flag = (flags & FLAG_NOT_TIMESLICE) != 0;
110 let ts_gs_mode = flags & TS_GS_MODE_MASK;
111
112 let mut pos = HEADER_LEN + FLAGS_LEN;
113
114 let scrambling_sequence_index = if scrambling_sequence_selector {
115 if pos + SCRAMBLING_FIELD_LEN > end {
116 return Err(Error::BufferTooShort {
117 need: pos + SCRAMBLING_FIELD_LEN - HEADER_LEN,
118 have: length,
119 what: "S2SatelliteDeliverySystemDescriptor scrambling_sequence_index",
120 });
121 }
122 let b0 = bytes[pos];
123 let index = (u32::from(b0 & SCRAMBLING_INDEX_HI_MASK) << 16)
124 | (u32::from(bytes[pos + 1]) << 8)
125 | u32::from(bytes[pos + 2]);
126 pos += SCRAMBLING_FIELD_LEN;
127 Some(index)
128 } else {
129 None
130 };
131
132 let input_stream_identifier = if multiple_input_stream_flag {
133 if pos + ISI_FIELD_LEN > end {
134 return Err(Error::BufferTooShort {
135 need: pos + ISI_FIELD_LEN - HEADER_LEN,
136 have: length,
137 what: "S2SatelliteDeliverySystemDescriptor input_stream_identifier",
138 });
139 }
140 let isi = bytes[pos];
141 pos += ISI_FIELD_LEN;
142 Some(isi)
143 } else {
144 None
145 };
146
147 let timeslice_number = if !not_timeslice_flag {
149 if pos + TIMESLICE_FIELD_LEN > end {
150 return Err(Error::BufferTooShort {
151 need: pos + TIMESLICE_FIELD_LEN - HEADER_LEN,
152 have: length,
153 what: "S2SatelliteDeliverySystemDescriptor timeslice_number",
154 });
155 }
156 Some(bytes[pos])
157 } else {
158 None
159 };
160
161 Ok(Self {
162 scrambling_sequence_selector,
163 multiple_input_stream_flag,
164 not_timeslice_flag,
165 ts_gs_mode,
166 scrambling_sequence_index,
167 input_stream_identifier,
168 timeslice_number,
169 })
170 }
171}
172
173impl Serialize for S2SatelliteDeliverySystemDescriptor {
174 type Error = crate::error::Error;
175 fn serialized_len(&self) -> usize {
176 HEADER_LEN
177 + FLAGS_LEN
178 + if self.scrambling_sequence_selector {
179 SCRAMBLING_FIELD_LEN
180 } else {
181 0
182 }
183 + if self.multiple_input_stream_flag {
184 ISI_FIELD_LEN
185 } else {
186 0
187 }
188 + if self.not_timeslice_flag {
189 0
190 } else {
191 TIMESLICE_FIELD_LEN
192 }
193 }
194
195 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
196 let len = self.serialized_len();
197 if buf.len() < len {
198 return Err(Error::OutputBufferTooSmall {
199 need: len,
200 have: buf.len(),
201 });
202 }
203 buf[0] = TAG;
204 buf[1] = (len - HEADER_LEN) as u8;
205
206 let mut flags = FLAG_RESERVED_BITS | (self.ts_gs_mode & TS_GS_MODE_MASK);
207 if self.scrambling_sequence_selector {
208 flags |= FLAG_SCRAMBLING_SELECTOR;
209 }
210 if self.multiple_input_stream_flag {
211 flags |= FLAG_MULTIPLE_INPUT_STREAM;
212 }
213 if self.not_timeslice_flag {
214 flags |= FLAG_NOT_TIMESLICE;
215 }
216 buf[HEADER_LEN] = flags;
217
218 let mut pos = HEADER_LEN + FLAGS_LEN;
219 if self.scrambling_sequence_selector {
220 let index = self.scrambling_sequence_index.unwrap_or(0) & SCRAMBLING_INDEX_MAX;
221 buf[pos] = SCRAMBLING_RESERVED_MASK | ((index >> 16) as u8 & SCRAMBLING_INDEX_HI_MASK);
222 buf[pos + 1] = (index >> 8) as u8;
223 buf[pos + 2] = index as u8;
224 pos += SCRAMBLING_FIELD_LEN;
225 }
226 if self.multiple_input_stream_flag {
227 buf[pos] = self.input_stream_identifier.unwrap_or(0);
228 pos += ISI_FIELD_LEN;
229 }
230 if !self.not_timeslice_flag {
231 buf[pos] = self.timeslice_number.unwrap_or(0);
232 }
233 Ok(len)
234 }
235}
236
237impl<'a> Descriptor<'a> for S2SatelliteDeliverySystemDescriptor {
238 const TAG: u8 = TAG;
239 fn descriptor_length(&self) -> u8 {
240 (self.serialized_len() - HEADER_LEN) as u8
241 }
242}
243
244impl<'a> crate::traits::DescriptorDef<'a> for S2SatelliteDeliverySystemDescriptor {
245 const TAG: u8 = TAG;
246 const NAME: &'static str = "S2_SATELLITE_DELIVERY_SYSTEM";
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
256 fn parse_minimal_with_timeslice() {
257 let raw = [TAG, 2, 0x2C, 0x07];
258 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
259 assert!(!d.scrambling_sequence_selector);
260 assert!(!d.multiple_input_stream_flag);
261 assert!(!d.not_timeslice_flag);
262 assert_eq!(d.ts_gs_mode, 0);
263 assert_eq!(d.timeslice_number, Some(0x07));
264 assert_eq!(d.scrambling_sequence_index, None);
265 assert_eq!(d.input_stream_identifier, None);
266 }
267
268 #[test]
269 fn parse_not_timeslice_omits_timeslice_number() {
270 let raw = [TAG, 1, 0x3E];
272 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
273 assert!(d.not_timeslice_flag);
274 assert_eq!(d.ts_gs_mode, 2);
275 assert_eq!(d.timeslice_number, None);
276 }
277
278 #[test]
279 fn parse_extracts_isi_and_timeslice() {
280 let raw = [TAG, 3, 0x6C, 0x05, 0x09];
282 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
283 assert!(d.multiple_input_stream_flag);
284 assert_eq!(d.input_stream_identifier, Some(5));
285 assert_eq!(d.timeslice_number, Some(9));
286 }
287
288 #[test]
289 fn parse_extracts_scrambling_index() {
290 let raw = [TAG, 4, 0xBC, 0xFD, 0x23, 0x45];
292 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
293 assert!(d.scrambling_sequence_selector);
294 assert_eq!(d.scrambling_sequence_index, Some(0x12345));
295 assert!(d.not_timeslice_flag);
296 assert_eq!(d.timeslice_number, None);
297 }
298
299 #[test]
300 fn parse_full_18_bit_index() {
301 let raw = [TAG, 4, 0xBC, 0xFF, 0xFF, 0xFF];
302 let d = S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap();
303 assert_eq!(d.scrambling_sequence_index, Some(0x3FFFF));
304 }
305
306 #[test]
307 fn parse_rejects_wrong_tag() {
308 let raw = [0x44, 1, 0x3E];
309 assert!(matches!(
310 S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap_err(),
311 Error::InvalidDescriptor { tag: 0x44, .. }
312 ));
313 }
314
315 #[test]
316 fn parse_rejects_truncated_scrambling() {
317 let raw = [TAG, 1, 0x9C]; assert!(matches!(
319 S2SatelliteDeliverySystemDescriptor::parse(&raw).unwrap_err(),
320 Error::BufferTooShort { .. }
321 ));
322 }
323
324 #[test]
325 fn serialize_round_trip_all_fields() {
326 let d = S2SatelliteDeliverySystemDescriptor {
327 scrambling_sequence_selector: true,
328 multiple_input_stream_flag: true,
329 not_timeslice_flag: false,
330 ts_gs_mode: 2,
331 scrambling_sequence_index: Some(0x2BCDE),
332 input_stream_identifier: Some(0x42),
333 timeslice_number: Some(0x11),
334 };
335 let mut buf = vec![0u8; d.serialized_len()];
336 d.serialize_into(&mut buf).unwrap();
337 assert_eq!(S2SatelliteDeliverySystemDescriptor::parse(&buf).unwrap(), d);
338 }
339
340 #[test]
341 fn serialize_round_trip_not_timeslice() {
342 let d = S2SatelliteDeliverySystemDescriptor {
343 scrambling_sequence_selector: false,
344 multiple_input_stream_flag: false,
345 not_timeslice_flag: true,
346 ts_gs_mode: 1,
347 scrambling_sequence_index: None,
348 input_stream_identifier: None,
349 timeslice_number: None,
350 };
351 let mut buf = vec![0u8; d.serialized_len()];
352 d.serialize_into(&mut buf).unwrap();
353 assert_eq!(S2SatelliteDeliverySystemDescriptor::parse(&buf).unwrap(), d);
354 }
355}