1use alloc::vec::Vec;
11
12use crate::error::{Error, Result};
13use crate::ext_header::PayloadChain;
14use crate::type_field::TypeField;
15
16pub const BASE_HEADER_LEN: usize = 4;
18pub const NPA_LEN: usize = 6;
20pub const CRC_LEN: usize = 4;
22pub const END_INDICATOR_LENGTH: u16 = 0x7FFF;
24pub const END_INDICATOR: u16 = 0xFFFF;
26pub const PADDING_BYTE: u8 = 0xFF;
28
29pub(crate) const D_BIT_MASK: u16 = 0x8000;
32pub(crate) const LENGTH_MASK: u16 = 0x7FFF;
34
35#[derive(Debug, Clone, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize))]
47pub struct Sndu<'a> {
48 pub dest_address: Option<[u8; NPA_LEN]>,
51 pub payload: PayloadChain<'a>,
54}
55
56impl<'a> Sndu<'a> {
57 pub fn new(type_field: TypeField, dest_address: Option<[u8; NPA_LEN]>, pdu: &'a [u8]) -> Self {
60 Sndu {
61 dest_address,
62 payload: PayloadChain {
63 headers: Vec::new(),
64 final_type: type_field,
65 pdu,
66 },
67 }
68 }
69
70 pub fn type_field(&self) -> TypeField {
74 self.payload.base_type()
75 }
76
77 pub fn d_bit(&self) -> bool {
79 self.dest_address.is_none()
80 }
81
82 pub fn pdu(&self) -> &'a [u8] {
84 self.payload.pdu
85 }
86
87 pub fn length_field(&self) -> usize {
91 let npa = if self.dest_address.is_some() {
92 NPA_LEN
93 } else {
94 0
95 };
96 npa + self.payload.serialized_len() + CRC_LEN
97 }
98
99 pub fn serialized_len(&self) -> usize {
102 BASE_HEADER_LEN + self.length_field()
103 }
104
105 pub fn parse(data: &'a [u8]) -> Result<Self> {
109 if data.len() < BASE_HEADER_LEN {
110 return Err(Error::BufferTooShort {
111 need: BASE_HEADER_LEN,
112 have: data.len(),
113 what: "SNDU base header",
114 });
115 }
116 let first = u16::from_be_bytes([data[0], data[1]]);
117 let d_bit = (first & D_BIT_MASK) != 0;
118 let length = first & LENGTH_MASK;
119 let raw_type = u16::from_be_bytes([data[2], data[3]]);
120 let type_field = TypeField::from_u16(raw_type);
121
122 let region_end = BASE_HEADER_LEN + length as usize;
124 if length as usize > data.len() - BASE_HEADER_LEN {
125 return Err(Error::InvalidLength {
126 length,
127 reason: "Length field exceeds available bytes",
128 });
129 }
130 if (length as usize) < CRC_LEN {
131 return Err(Error::InvalidLength {
132 length,
133 reason: "Length shorter than the CRC trailer",
134 });
135 }
136
137 let dest_address = if d_bit {
138 None
139 } else {
140 if BASE_HEADER_LEN + NPA_LEN > region_end {
141 return Err(Error::InvalidLength {
142 length,
143 reason: "Length cannot hold the NPA address + CRC",
144 });
145 }
146 let mut npa = [0u8; NPA_LEN];
147 npa.copy_from_slice(&data[BASE_HEADER_LEN..BASE_HEADER_LEN + NPA_LEN]);
148 Some(npa)
149 };
150
151 let payload_start = BASE_HEADER_LEN + if d_bit { 0 } else { NPA_LEN };
152 let crc_start = region_end - CRC_LEN;
153 if crc_start < payload_start {
154 return Err(Error::InvalidLength {
155 length,
156 reason: "Length leaves no room for the payload",
157 });
158 }
159
160 let computed = dvb_common::crc32_mpeg2::compute(&data[..crc_start]);
162 let found = u32::from_be_bytes([
163 data[crc_start],
164 data[crc_start + 1],
165 data[crc_start + 2],
166 data[crc_start + 3],
167 ]);
168 if computed != found {
169 return Err(Error::CrcMismatch { computed, found });
170 }
171
172 let payload = PayloadChain::parse(type_field, &data[payload_start..crc_start])?;
173
174 Ok(Sndu {
175 dest_address,
176 payload,
177 })
178 }
179
180 pub fn serialize_into(&self, out: &mut [u8]) -> Result<usize> {
183 let total = self.serialized_len();
184 if out.len() < total {
185 return Err(Error::OutputBufferTooSmall {
186 need: total,
187 have: out.len(),
188 });
189 }
190
191 let length = self.length_field();
192 if length > END_INDICATOR_LENGTH as usize {
193 return Err(Error::FieldTooWide {
194 what: "Length",
195 value: length as u32,
196 bits: 15,
197 });
198 }
199
200 let base_type = self.payload.base_type();
202
203 let mut first = length as u16 & LENGTH_MASK;
205 if self.d_bit() {
206 first |= D_BIT_MASK;
207 }
208 out[0..2].copy_from_slice(&first.to_be_bytes());
209 out[2..4].copy_from_slice(&base_type.to_u16().to_be_bytes());
211
212 let mut off = BASE_HEADER_LEN;
213 if let Some(npa) = self.dest_address {
214 out[off..off + NPA_LEN].copy_from_slice(&npa);
215 off += NPA_LEN;
216 }
217 let written = self
218 .payload
219 .serialize_into(&mut out[off..total - CRC_LEN])?;
220 off += written;
221
222 let crc = dvb_common::crc32_mpeg2::compute(&out[..off]);
224 out[off..off + CRC_LEN].copy_from_slice(&crc.to_be_bytes());
225 off += CRC_LEN;
226 Ok(off)
227 }
228}
229
230pub fn is_end_indicator(data: &[u8]) -> bool {
234 data.len() >= 2 && data[0] == PADDING_BYTE && data[1] == PADDING_BYTE
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use alloc::vec;
241
242 #[test]
245 fn d0_with_npa_exact_wire_bytes() {
246 let pdu = [0x45u8, 0x00, 0x00, 0x14, 0xAB, 0xCD];
247 let npa = [0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06];
248 let sndu = Sndu::new(TypeField::EtherType(0x0800), Some(npa), &pdu);
249
250 assert_eq!(sndu.length_field(), 16);
252 let mut out = vec![0u8; sndu.serialized_len()];
253 let n = sndu.serialize_into(&mut out).unwrap();
254 assert_eq!(n, 4 + 16);
255
256 assert_eq!(&out[0..2], &[0x00, 0x10]);
258 assert_eq!(&out[2..4], &[0x08, 0x00]);
259 assert_eq!(&out[4..10], &npa);
261 assert_eq!(&out[10..16], &pdu);
262 let crc = dvb_common::crc32_mpeg2::compute(&out[..16]);
264 assert_eq!(&out[16..20], &crc.to_be_bytes());
265
266 assert_eq!(Sndu::parse(&out).unwrap(), sndu);
268 }
269
270 #[test]
272 fn d1_without_npa_exact_wire_bytes() {
273 let pdu = [0x60u8, 0x00, 0x00, 0x00];
274 let sndu = Sndu::new(TypeField::EtherType(0x86DD), None, &pdu);
275
276 assert_eq!(sndu.length_field(), 8);
278 let mut out = vec![0u8; sndu.serialized_len()];
279 sndu.serialize_into(&mut out).unwrap();
280
281 assert_eq!(&out[0..2], &[0x80, 0x08]);
283 assert_eq!(&out[2..4], &[0x86, 0xDD]);
284 assert_eq!(&out[4..8], &pdu);
285 let crc = dvb_common::crc32_mpeg2::compute(&out[..8]);
286 assert_eq!(&out[8..12], &crc.to_be_bytes());
287
288 assert_eq!(Sndu::parse(&out).unwrap(), sndu);
289 }
290
291 #[test]
294 fn mutating_a_field_changes_crc() {
295 let pdu = [0xDEu8, 0xAD, 0xBE, 0xEF];
296 let a = Sndu::new(TypeField::EtherType(0x0800), None, &pdu);
297 let mut buf_a = vec![0u8; a.serialized_len()];
298 a.serialize_into(&mut buf_a).unwrap();
299
300 let pdu2 = [0xDEu8, 0xAD, 0xBE, 0xEE]; let b = Sndu::new(TypeField::EtherType(0x0800), None, &pdu2);
302 let mut buf_b = vec![0u8; b.serialized_len()];
303 b.serialize_into(&mut buf_b).unwrap();
304
305 assert_ne!(
306 buf_a, buf_b,
307 "different PDU must yield different wire bytes"
308 );
309 assert_ne!(buf_a[8..12], buf_b[8..12]);
311 }
312
313 #[test]
314 fn npa_presence_drives_d_bit_and_length() {
315 let pdu = [0u8; 10];
316 let with = Sndu::new(TypeField::EtherType(0x0800), Some([0xFF; 6]), &pdu);
317 let without = Sndu::new(TypeField::EtherType(0x0800), None, &pdu);
318 assert!(!with.d_bit());
319 assert!(without.d_bit());
320 assert_eq!(with.length_field(), without.length_field() + NPA_LEN);
321 }
322
323 #[test]
324 fn rejects_bad_crc() {
325 let pdu = [1u8, 2, 3, 4];
326 let sndu = Sndu::new(TypeField::EtherType(0x0800), None, &pdu);
327 let mut buf = vec![0u8; sndu.serialized_len()];
328 sndu.serialize_into(&mut buf).unwrap();
329 let last = buf.len() - 1;
330 buf[last] ^= 0x01;
331 assert!(matches!(Sndu::parse(&buf), Err(Error::CrcMismatch { .. })));
332 }
333
334 #[test]
335 fn rejects_length_overrun() {
336 let data = [0x00u8, 0x64, 0x08, 0x00, 0x00];
338 assert!(matches!(
339 Sndu::parse(&data),
340 Err(Error::InvalidLength { .. })
341 ));
342 }
343
344 #[test]
345 fn end_indicator_detected() {
346 assert!(is_end_indicator(&[0xFF, 0xFF, 0xFF]));
347 assert!(!is_end_indicator(&[0x00, 0x10]));
348 }
349
350 #[test]
353 fn type_field_accessor_matches_serialized_wire() {
354 let pdu = [0xAAu8; 4];
355 let sndu = Sndu::new(TypeField::EtherType(0x0800), None, &pdu);
356 assert_eq!(sndu.type_field(), TypeField::EtherType(0x0800));
357 let mut buf = vec![0u8; sndu.serialized_len()];
359 sndu.serialize_into(&mut buf).unwrap();
360 let wire_type = u16::from_be_bytes([buf[2], buf[3]]);
361 assert_eq!(sndu.type_field().to_u16(), wire_type);
362 }
363}