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