1use super::descriptor_body;
8use crate::error::{Error, Result};
9use crate::text::{DvbText, LangCode};
10use dvb_common::{Parse, Serialize};
11
12pub const TAG: u8 = 0x64;
14const HEADER_LEN: usize = 2;
15const ID_LEN: usize = 2;
16const COMPONENT_TAG_LEN: usize = 1;
17const SELECTOR_LEN_FIELD: usize = 1;
18const LANG_LEN: usize = 3;
19const TEXT_LEN_FIELD: usize = 1;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize))]
24#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
25pub struct DataBroadcastDescriptor<'a> {
26 pub data_broadcast_id: u16,
28 pub component_tag: u8,
30 pub selector: &'a [u8],
33 pub language_code: LangCode,
35 pub text: DvbText<'a>,
37}
38
39impl<'a> Parse<'a> for DataBroadcastDescriptor<'a> {
40 type Error = crate::error::Error;
41 fn parse(bytes: &'a [u8]) -> Result<Self> {
42 let min_body = ID_LEN + COMPONENT_TAG_LEN + SELECTOR_LEN_FIELD + LANG_LEN + TEXT_LEN_FIELD;
43 let body = descriptor_body(
44 bytes,
45 TAG,
46 "DataBroadcastDescriptor",
47 "unexpected tag for data_broadcast_descriptor",
48 )?;
49 if body.len() < min_body {
50 return Err(Error::InvalidDescriptor {
51 tag: TAG,
52 reason: "data_broadcast_descriptor body shorter than minimum 8 bytes",
53 });
54 }
55 let mut pos = 0;
56 let data_broadcast_id = u16::from_be_bytes([body[pos], body[pos + 1]]);
57 pos += ID_LEN;
58 let component_tag = body[pos];
59 pos += COMPONENT_TAG_LEN;
60
61 let selector_length = body[pos] as usize;
62 pos += SELECTOR_LEN_FIELD;
63 let selector_end = pos + selector_length;
64 if selector_end + LANG_LEN + TEXT_LEN_FIELD > body.len() {
66 return Err(Error::InvalidDescriptor {
67 tag: TAG,
68 reason: "selector_length runs past descriptor end",
69 });
70 }
71 let selector = &body[pos..selector_end];
72 pos = selector_end;
73
74 let language_code = LangCode([body[pos], body[pos + 1], body[pos + 2]]);
75 pos += LANG_LEN;
76
77 let text_length = body[pos] as usize;
78 pos += TEXT_LEN_FIELD;
79 let text_end = pos + text_length;
80 if text_end > body.len() {
81 return Err(Error::InvalidDescriptor {
82 tag: TAG,
83 reason: "text_length runs past descriptor end",
84 });
85 }
86 let text = DvbText::new(&body[pos..text_end]);
87
88 Ok(Self {
89 data_broadcast_id,
90 component_tag,
91 selector,
92 language_code,
93 text,
94 })
95 }
96}
97
98impl Serialize for DataBroadcastDescriptor<'_> {
99 type Error = crate::error::Error;
100 fn serialized_len(&self) -> usize {
101 HEADER_LEN
102 + ID_LEN
103 + COMPONENT_TAG_LEN
104 + SELECTOR_LEN_FIELD
105 + self.selector.len()
106 + LANG_LEN
107 + TEXT_LEN_FIELD
108 + self.text.len()
109 }
110
111 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
112 if self.selector.len() > u8::MAX as usize {
113 return Err(Error::InvalidDescriptor {
114 tag: TAG,
115 reason: "selector exceeds 255 bytes (selector_length is 8-bit)",
116 });
117 }
118 if self.text.len() > u8::MAX as usize {
119 return Err(Error::InvalidDescriptor {
120 tag: TAG,
121 reason: "text exceeds 255 bytes (text_length is 8-bit)",
122 });
123 }
124 let len = self.serialized_len();
125 let body = len - HEADER_LEN;
126 if body > u8::MAX as usize {
127 return Err(Error::InvalidDescriptor {
128 tag: TAG,
129 reason: "data_broadcast_descriptor body exceeds 255 bytes",
130 });
131 }
132 if buf.len() < len {
133 return Err(Error::OutputBufferTooSmall {
134 need: len,
135 have: buf.len(),
136 });
137 }
138 buf[0] = TAG;
139 buf[1] = body as u8;
140 let mut pos = HEADER_LEN;
141 buf[pos..pos + ID_LEN].copy_from_slice(&self.data_broadcast_id.to_be_bytes());
142 pos += ID_LEN;
143 buf[pos] = self.component_tag;
144 pos += COMPONENT_TAG_LEN;
145 buf[pos] = self.selector.len() as u8;
146 pos += SELECTOR_LEN_FIELD;
147 buf[pos..pos + self.selector.len()].copy_from_slice(self.selector);
148 pos += self.selector.len();
149 buf[pos..pos + LANG_LEN].copy_from_slice(&self.language_code.0);
150 pos += LANG_LEN;
151 buf[pos] = self.text.len() as u8;
152 pos += TEXT_LEN_FIELD;
153 buf[pos..pos + self.text.len()].copy_from_slice(self.text.raw());
154 Ok(len)
155 }
156}
157impl<'a> crate::traits::DescriptorDef<'a> for DataBroadcastDescriptor<'a> {
158 const TAG: u8 = TAG;
159 const NAME: &'static str = "DATA_BROADCAST";
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 fn build(id: u16, ctag: u8, selector: &[u8], lang: [u8; 3], text: &[u8]) -> Vec<u8> {
167 let body = ID_LEN
168 + COMPONENT_TAG_LEN
169 + SELECTOR_LEN_FIELD
170 + selector.len()
171 + LANG_LEN
172 + TEXT_LEN_FIELD
173 + text.len();
174 let mut v = Vec::with_capacity(HEADER_LEN + body);
175 v.push(TAG);
176 v.push(body as u8);
177 v.extend_from_slice(&id.to_be_bytes());
178 v.push(ctag);
179 v.push(selector.len() as u8);
180 v.extend_from_slice(selector);
181 v.extend_from_slice(&lang);
182 v.push(text.len() as u8);
183 v.extend_from_slice(text);
184 v
185 }
186
187 #[test]
188 fn parse_extracts_all_fields() {
189 let bytes = build(0x000B, 0x12, &[0xAA, 0xBB], *b"eng", b"Hello");
190 let d = DataBroadcastDescriptor::parse(&bytes).unwrap();
191 assert_eq!(d.data_broadcast_id, 0x000B);
192 assert_eq!(d.component_tag, 0x12);
193 assert_eq!(d.selector, &[0xAA, 0xBB]);
194 assert_eq!(d.language_code, LangCode(*b"eng"));
195 assert_eq!(d.text.raw(), b"Hello");
196 }
197
198 #[test]
199 fn parse_accepts_empty_selector_and_text() {
200 let bytes = build(0x0001, 0x00, &[], *b"fra", b"");
201 let d = DataBroadcastDescriptor::parse(&bytes).unwrap();
202 assert!(d.selector.is_empty());
203 assert!(d.text.raw().is_empty());
204 assert_eq!(d.language_code, LangCode(*b"fra"));
205 }
206
207 #[test]
208 fn parse_rejects_wrong_tag() {
209 let mut bytes = build(0x0001, 0x00, &[], *b"eng", b"");
210 bytes[0] = 0x65;
211 let err = DataBroadcastDescriptor::parse(&bytes).unwrap_err();
212 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x65, .. }));
213 }
214
215 #[test]
216 fn parse_rejects_short_buffer() {
217 let err = DataBroadcastDescriptor::parse(&[TAG]).unwrap_err();
218 assert!(matches!(err, Error::BufferTooShort { .. }));
219 }
220
221 #[test]
222 fn parse_rejects_body_too_short() {
223 let err = DataBroadcastDescriptor::parse(&[TAG, 4, 0, 0, 0, 0]).unwrap_err();
225 assert!(matches!(err, Error::InvalidDescriptor { .. }));
226 }
227
228 #[test]
229 fn parse_rejects_selector_length_overrun() {
230 let bytes = [TAG, 8, 0x00, 0x0B, 0x12, 200, b'e', b'n', b'g', 0];
232 let err = DataBroadcastDescriptor::parse(&bytes).unwrap_err();
233 assert!(matches!(err, Error::InvalidDescriptor { .. }));
234 }
235
236 #[test]
237 fn parse_rejects_text_length_overrun() {
238 let bytes = [TAG, 8, 0x00, 0x0B, 0x12, 0, b'e', b'n', b'g', 5];
240 let err = DataBroadcastDescriptor::parse(&bytes).unwrap_err();
241 assert!(matches!(err, Error::InvalidDescriptor { .. }));
242 }
243
244 #[test]
245 fn serialize_round_trip() {
246 let bytes = build(0x0123, 0x45, &[0xDE, 0xAD], *b"deu", b"Daten");
247 let parsed = DataBroadcastDescriptor::parse(&bytes).unwrap();
248 let mut buf = vec![0u8; parsed.serialized_len()];
249 parsed.serialize_into(&mut buf).unwrap();
250 assert_eq!(buf, bytes);
251 let re = DataBroadcastDescriptor::parse(&buf).unwrap();
252 assert_eq!(parsed, re);
253 }
254
255 #[test]
256 fn serialize_rejects_too_small_buffer() {
257 let d = DataBroadcastDescriptor {
258 data_broadcast_id: 0x0001,
259 component_tag: 0x00,
260 selector: &[],
261 language_code: LangCode(*b"eng"),
262 text: DvbText::new(&[]),
263 };
264 let mut tiny = [0u8; 4];
265 let err = d.serialize_into(&mut tiny).unwrap_err();
266 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
267 }
268
269 #[test]
270 fn serialize_rejects_over_range_selector() {
271 let sel = vec![0u8; 256];
272 let d = DataBroadcastDescriptor {
273 data_broadcast_id: 0x0001,
274 component_tag: 0x00,
275 selector: &sel,
276 language_code: LangCode(*b"eng"),
277 text: DvbText::new(&[]),
278 };
279 let mut buf = vec![0u8; d.serialized_len()];
280 let err = d.serialize_into(&mut buf).unwrap_err();
281 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
282 }
283
284 #[test]
285 fn serialize_rejects_over_range_body() {
286 let sel = vec![0u8; 250];
288 let txt = vec![0u8; 10];
289 let d = DataBroadcastDescriptor {
290 data_broadcast_id: 0x0001,
291 component_tag: 0x00,
292 selector: &sel,
293 language_code: LangCode(*b"eng"),
294 text: DvbText::new(&txt),
295 };
296 let mut buf = vec![0u8; d.serialized_len()];
297 let err = d.serialize_into(&mut buf).unwrap_err();
298 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
299 }
300
301 #[cfg(feature = "serde")]
302 #[test]
303 fn serde_serialize_is_stable() {
304 let d = DataBroadcastDescriptor {
305 data_broadcast_id: 0x000B,
306 component_tag: 0x09,
307 selector: &[0x01, 0x02],
308 language_code: LangCode(*b"eng"),
309 text: DvbText::new(b"Text"),
310 };
311 let json = serde_json::to_string(&d).unwrap();
312 assert!(json.contains("\"data_broadcast_id\""));
313 assert!(json.contains("\"component_tag\""));
314 assert!(json.contains("\"eng\""));
315 assert!(json.contains("\"Text\""));
316 }
317}