dvb_si/descriptors/extension/
target_region.rs1use super::*;
3use alloc::vec::Vec;
4
5impl<'a> ExtensionBodyDef<'a> for TargetRegion {
6 const TAG_EXTENSION: u8 = 0x09;
7 const NAME: &'static str = "TARGET_REGION";
8}
9#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize))]
12pub struct TargetRegion {
13 pub country_code: LangCode,
15 pub regions: Vec<TargetRegionEntry>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct TargetRegionEntry {
23 pub country_code: Option<LangCode>,
25 pub region_codes: RegionCodes,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32#[non_exhaustive]
33pub enum RegionCodes {
34 None,
36 Primary {
38 primary_region_code: u8,
40 },
41 PrimarySecondary {
43 primary_region_code: u8,
45 secondary_region_code: u8,
47 },
48 Full {
50 primary_region_code: u8,
52 secondary_region_code: u8,
54 tertiary_region_code: u16,
56 },
57}
58
59impl<'a> Parse<'a> for TargetRegion {
60 type Error = crate::error::Error;
61 fn parse(sel: &'a [u8]) -> Result<Self> {
62 if sel.len() < ISO_639_LEN {
63 return Err(Error::BufferTooShort {
64 need: ISO_639_LEN,
65 have: sel.len(),
66 what: "target_region body",
67 });
68 }
69 let country_code = LangCode([sel[0], sel[1], sel[2]]);
70 let regions = parse_region_entries(sel, ISO_639_LEN)?;
71 Ok(TargetRegion {
72 country_code,
73 regions,
74 })
75 }
76}
77
78pub(crate) fn parse_region_entries(sel: &[u8], start: usize) -> Result<Vec<TargetRegionEntry>> {
84 let mut regions = Vec::new();
85 let mut pos = start;
86 while pos < sel.len() {
87 let flags = sel[pos];
88 pos += 1;
89 let country_code_flag = (flags >> 2) & 1;
90 let region_depth = flags & 0x03;
91 let country_code = if country_code_flag == 1 {
92 if pos + ISO_639_LEN > sel.len() {
93 return Err(Error::BufferTooShort {
94 need: pos + ISO_639_LEN,
95 have: sel.len(),
96 what: "target_region body",
97 });
98 }
99 let cc = LangCode([sel[pos], sel[pos + 1], sel[pos + 2]]);
100 pos += ISO_639_LEN;
101 Some(cc)
102 } else {
103 None
104 };
105 let region_codes = match region_depth {
106 0 => RegionCodes::None,
107 1 => {
108 if pos >= sel.len() {
109 return Err(Error::BufferTooShort {
110 need: pos + 1,
111 have: sel.len(),
112 what: "target_region body",
113 });
114 }
115 let primary = sel[pos];
116 pos += 1;
117 RegionCodes::Primary {
118 primary_region_code: primary,
119 }
120 }
121 2 => {
122 if pos + 1 >= sel.len() {
123 return Err(Error::BufferTooShort {
124 need: pos + 2,
125 have: sel.len(),
126 what: "target_region body",
127 });
128 }
129 let primary = sel[pos];
130 let secondary = sel[pos + 1];
131 pos += 2;
132 RegionCodes::PrimarySecondary {
133 primary_region_code: primary,
134 secondary_region_code: secondary,
135 }
136 }
137 3 => {
138 if pos + 3 >= sel.len() {
139 return Err(Error::BufferTooShort {
140 need: pos + 4,
141 have: sel.len(),
142 what: "target_region body",
143 });
144 }
145 let primary = sel[pos];
146 let secondary = sel[pos + 1];
147 let (tert_bytes, _) =
148 sel[pos + 2..]
149 .split_first_chunk::<2>()
150 .ok_or(Error::BufferTooShort {
151 need: pos + 4,
152 have: sel.len(),
153 what: "target_region body",
154 })?;
155 let tertiary = u16::from_be_bytes(*tert_bytes);
156 pos += 4;
157 RegionCodes::Full {
158 primary_region_code: primary,
159 secondary_region_code: secondary,
160 tertiary_region_code: tertiary,
161 }
162 }
163 _ => return Err(invalid("target_region: invalid region_depth")),
164 };
165 regions.push(TargetRegionEntry {
166 country_code,
167 region_codes,
168 });
169 }
170 Ok(regions)
171}
172
173pub(crate) fn region_entries_serialized_len(entries: &[TargetRegionEntry]) -> usize {
176 entries
177 .iter()
178 .map(|r| {
179 1 + if r.country_code.is_some() {
180 ISO_639_LEN
181 } else {
182 0
183 } + match &r.region_codes {
184 RegionCodes::None => 0,
185 RegionCodes::Primary { .. } => 1,
186 RegionCodes::PrimarySecondary { .. } => 2,
187 RegionCodes::Full { .. } => 4,
188 }
189 })
190 .sum()
191}
192
193pub(crate) fn write_region_entries(
196 entries: &[TargetRegionEntry],
197 buf: &mut [u8],
198 mut pos: usize,
199) -> usize {
200 let start = pos;
201 for region in entries {
202 let depth = match ®ion.region_codes {
203 RegionCodes::None => 0u8,
204 RegionCodes::Primary { .. } => 1,
205 RegionCodes::PrimarySecondary { .. } => 2,
206 RegionCodes::Full { .. } => 3,
207 };
208 buf[pos] = 0xF8 | ((region.country_code.is_some() as u8) << 2) | depth;
209 pos += 1;
210 if let Some(cc) = ®ion.country_code {
211 buf[pos..pos + ISO_639_LEN].copy_from_slice(&cc.0);
212 pos += ISO_639_LEN;
213 }
214 match ®ion.region_codes {
215 RegionCodes::None => {}
216 RegionCodes::Primary {
217 primary_region_code,
218 } => {
219 buf[pos] = *primary_region_code;
220 pos += 1;
221 }
222 RegionCodes::PrimarySecondary {
223 primary_region_code,
224 secondary_region_code,
225 } => {
226 buf[pos] = *primary_region_code;
227 buf[pos + 1] = *secondary_region_code;
228 pos += 2;
229 }
230 RegionCodes::Full {
231 primary_region_code,
232 secondary_region_code,
233 tertiary_region_code,
234 } => {
235 buf[pos] = *primary_region_code;
236 buf[pos + 1] = *secondary_region_code;
237 buf[pos + 2..pos + 4].copy_from_slice(&tertiary_region_code.to_be_bytes());
238 pos += 4;
239 }
240 }
241 }
242 pos - start
243}
244
245impl Serialize for TargetRegion {
246 type Error = crate::error::Error;
247 fn serialized_len(&self) -> usize {
248 ISO_639_LEN + region_entries_serialized_len(&self.regions)
249 }
250 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
251 let len = self.serialized_len();
252 if buf.len() < len {
253 return Err(Error::OutputBufferTooSmall {
254 need: len,
255 have: buf.len(),
256 });
257 }
258 buf[..ISO_639_LEN].copy_from_slice(&self.country_code.0);
259 write_region_entries(&self.regions, buf, ISO_639_LEN);
260 Ok(len)
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267 use crate::descriptors::extension::test_support::*;
268 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
269 use crate::text::LangCode;
270
271 #[test]
272 fn parse_target_region_structured() {
273 let sel = [b'g', b'b', b'r', 0xF9, 0x12];
274 let bytes = wrap(0x09, &sel);
275 let d = ExtensionDescriptor::parse(&bytes).unwrap();
276 match &d.body {
277 ExtensionBody::TargetRegion(b) => {
278 assert_eq!(b.country_code, LangCode(*b"gbr"));
279 assert_eq!(b.regions.len(), 1);
280 assert_eq!(b.regions[0].country_code, None);
281 assert_eq!(
282 b.regions[0].region_codes,
283 RegionCodes::Primary {
284 primary_region_code: 0x12
285 }
286 );
287 }
288 other => panic!("expected TargetRegion, got {other:?}"),
289 }
290 round_trip(&d);
291 }
292
293 #[test]
294 fn target_region_tsduck_empty() {
295 let bytes = from_hex("7f0409666f6f");
296 let d = ExtensionDescriptor::parse(&bytes).unwrap();
297 match &d.body {
298 ExtensionBody::TargetRegion(b) => {
299 assert_eq!(b.country_code, LangCode(*b"foo"));
300 assert!(b.regions.is_empty());
301 }
302 other => panic!("expected TargetRegion, got {other:?}"),
303 }
304 let mut buf = vec![0u8; d.serialized_len()];
305 d.serialize_into(&mut buf).unwrap();
306 assert_eq!(buf, bytes);
307 }
308
309 #[test]
310 fn target_region_tsduck_full() {
311 let bytes = from_hex("7f1509626172f8fd666f6f12fa3456ff616263789abcde");
312 let d = ExtensionDescriptor::parse(&bytes).unwrap();
313 match &d.body {
314 ExtensionBody::TargetRegion(b) => {
315 assert_eq!(b.country_code, LangCode(*b"bar"));
316 assert_eq!(b.regions.len(), 4);
317 assert_eq!(b.regions[0].country_code, None);
319 assert_eq!(b.regions[0].region_codes, RegionCodes::None);
320 assert_eq!(b.regions[1].country_code, Some(LangCode(*b"foo")));
322 assert_eq!(
323 b.regions[1].region_codes,
324 RegionCodes::Primary {
325 primary_region_code: 0x12
326 }
327 );
328 assert_eq!(b.regions[2].country_code, None);
330 assert_eq!(
331 b.regions[2].region_codes,
332 RegionCodes::PrimarySecondary {
333 primary_region_code: 0x34,
334 secondary_region_code: 0x56,
335 }
336 );
337 assert_eq!(b.regions[3].country_code, Some(LangCode(*b"abc")));
339 assert_eq!(
340 b.regions[3].region_codes,
341 RegionCodes::Full {
342 primary_region_code: 0x78,
343 secondary_region_code: 0x9A,
344 tertiary_region_code: 0xBCDE,
345 }
346 );
347 }
348 other => panic!("expected TargetRegion, got {other:?}"),
349 }
350 let mut buf = vec![0u8; d.serialized_len()];
351 d.serialize_into(&mut buf).unwrap();
352 assert_eq!(buf, bytes);
353 }
354}