1use std::borrow::Cow;
18
19#[must_use]
23pub fn decode_dvb_string(bytes: &[u8]) -> String {
24 if bytes.is_empty() {
25 return String::new();
26 }
27
28 let (charset, body) = split_charset(bytes);
29 let decoded = match charset {
30 Charset::Iso6937 => decode_iso_6937(body),
31 Charset::Iso8859(n) => decode_iso_8859(n, body),
32 Charset::Utf8 => String::from_utf8_lossy(body).into_owned(),
33 Charset::Ucs2Be => decode_ucs2_be(body),
34 Charset::Ksx1001 => decode_with(encoding_rs::EUC_KR, body),
35 Charset::Gb2312 => decode_with(encoding_rs::GBK, body),
36 Charset::Big5 => decode_with(encoding_rs::BIG5, body),
37 Charset::Unsupported(_indicator) => body.iter().map(|_| '\u{FFFD}').collect(),
38 };
39
40 decoded
47 .chars()
48 .filter_map(|c| match c as u32 {
49 0x86 | 0x87 | 0xE086 | 0xE087 => None,
50 0x8A | 0xE08A => Some(' '),
51 0x0A => Some(' '),
52 code if code < 0x20 => None,
53 code if (0x80..0xA0).contains(&code) => None,
54 code if (0xE080..0xE0A0).contains(&code) => None,
55 _ => Some(c),
56 })
57 .collect()
58}
59
60#[must_use]
63pub fn decode(bytes: &[u8]) -> Cow<'_, str> {
64 if bytes.iter().all(|&b| b.is_ascii() && b >= 0x20) {
65 return Cow::Borrowed(std::str::from_utf8(bytes).unwrap_or(""));
66 }
67 Cow::Owned(decode_dvb_string(bytes))
68}
69
70#[derive(Debug)]
71enum Charset {
72 Iso6937,
73 Iso8859(u8),
74 Utf8,
75 Ucs2Be,
76 Ksx1001,
78 Gb2312,
80 Big5,
82 Unsupported(u8),
83}
84
85fn split_charset(bytes: &[u8]) -> (Charset, &[u8]) {
86 match bytes[0] {
87 b if b >= 0x20 => (Charset::Iso6937, bytes),
88 0x00 => (Charset::Iso6937, &bytes[1..]),
89 0x08 => (Charset::Unsupported(0x08), &bytes[1..]),
92 0x01..=0x0B => (Charset::Iso8859(bytes[0] + 4), &bytes[1..]),
93 0x10 if bytes.len() >= 3 && bytes[1] == 0x00 => (Charset::Iso8859(bytes[2]), &bytes[3..]),
94 0x11 => (Charset::Ucs2Be, &bytes[1..]),
95 0x12 => (Charset::Ksx1001, &bytes[1..]),
96 0x13 => (Charset::Gb2312, &bytes[1..]),
97 0x14 => (Charset::Big5, &bytes[1..]),
98 0x15 => (Charset::Utf8, &bytes[1..]),
99 0x1F if bytes.len() >= 2 => (Charset::Unsupported(0x1F), &bytes[2..]),
102 other => (Charset::Unsupported(other), &bytes[1..]),
103 }
104}
105
106fn decode_iso_6937(bytes: &[u8]) -> String {
107 let mut out = String::with_capacity(bytes.len());
108 let mut i = 0;
109 while i < bytes.len() {
110 let b = bytes[i];
111 if (0xC0..=0xCF).contains(&b) {
113 match combining_mark(b) {
114 Some(mark) if i + 1 < bytes.len() => {
115 let base = bytes[i + 1];
116 if let Some(c) = combine(b, base) {
117 out.push(c);
118 } else {
119 out.push(iso_6937_single(base));
122 out.push(mark);
123 }
124 i += 2;
125 }
126 _ => {
128 out.push('\u{FFFD}');
129 i += 1;
130 }
131 }
132 continue;
133 }
134 out.push(iso_6937_single(b));
135 i += 1;
136 }
137 out
138}
139
140fn iso_6937_single(b: u8) -> char {
148 match b {
149 0x00..=0x7F => b as char,
150 0x86 | 0x87 | 0x8A => b as char,
152 0x80..=0x9F => '\u{FFFD}',
153 0xA0 => '\u{00A0}', 0xA1 => '¡',
155 0xA2 => '¢',
156 0xA3 => '£',
157 0xA4 => '\u{20AC}', 0xA5 => '¥',
159 0xA6 => '\u{FFFD}', 0xA7 => '§',
161 0xA8 => '\u{00A4}', 0xA9 => '\u{2018}', 0xAA => '\u{201C}', 0xAB => '«',
165 0xAC => '\u{2190}', 0xAD => '\u{2191}', 0xAE => '\u{2192}', 0xAF => '\u{2193}', 0xB0 => '°',
170 0xB1 => '±',
171 0xB2 => '²',
172 0xB3 => '³',
173 0xB4 => '\u{00D7}', 0xB5 => 'µ',
175 0xB6 => '¶',
176 0xB7 => '·',
177 0xB8 => '\u{00F7}', 0xB9 => '\u{2019}', 0xBA => '\u{201D}', 0xBB => '»',
181 0xBC => '¼',
182 0xBD => '½',
183 0xBE => '¾',
184 0xBF => '¿',
185 0xC0..=0xCF => '\u{FFFD}',
187 0xD0 => '\u{2015}', 0xD1 => '¹',
189 0xD2 => '®',
190 0xD3 => '©',
191 0xD4 => '\u{2122}', 0xD5 => '\u{266A}', 0xD6 => '¬',
194 0xD7 => '\u{00A6}', 0xD8..=0xDB => '\u{FFFD}', 0xDC => '\u{215B}', 0xDD => '\u{215C}', 0xDE => '\u{215D}', 0xDF => '\u{215E}', 0xE0 => '\u{2126}', 0xE1 => 'Æ',
202 0xE2 => '\u{0110}', 0xE3 => 'ª',
204 0xE4 => '\u{0126}', 0xE5 => '\u{FFFD}', 0xE6 => '\u{0132}', 0xE7 => '\u{013F}', 0xE8 => '\u{0141}', 0xE9 => 'Ø',
210 0xEA => '\u{0152}', 0xEB => 'º',
212 0xEC => 'Þ',
213 0xED => '\u{0166}', 0xEE => '\u{014A}', 0xEF => '\u{0149}', 0xF0 => '\u{0138}', 0xF1 => 'æ',
218 0xF2 => '\u{0111}', 0xF3 => 'ð',
220 0xF4 => '\u{0127}', 0xF5 => '\u{0131}', 0xF6 => '\u{0133}', 0xF7 => '\u{0140}', 0xF8 => '\u{0142}', 0xF9 => 'ø',
226 0xFA => '\u{0153}', 0xFB => 'ß',
228 0xFC => '\u{00FE}', 0xFD => '\u{0167}', 0xFE => '\u{014B}', 0xFF => '\u{00AD}', }
233}
234
235fn combining_mark(prefix: u8) -> Option<char> {
238 Some(match prefix {
239 0xC1 => '\u{0300}', 0xC2 => '\u{0301}', 0xC3 => '\u{0302}', 0xC4 => '\u{0303}', 0xC5 => '\u{0304}', 0xC6 => '\u{0306}', 0xC7 => '\u{0307}', 0xC8 => '\u{0308}', 0xCA => '\u{030A}', 0xCB => '\u{0327}', 0xCD => '\u{030B}', 0xCE => '\u{0328}', 0xCF => '\u{030C}', _ => return None,
253 })
254}
255
256fn combine(prefix: u8, base: u8) -> Option<char> {
257 Some(match (prefix, base) {
258 (0xC1, b'A') => 'À',
259 (0xC1, b'E') => 'È',
260 (0xC1, b'I') => 'Ì',
261 (0xC1, b'O') => 'Ò',
262 (0xC1, b'U') => 'Ù',
263 (0xC1, b'a') => 'à',
264 (0xC1, b'e') => 'è',
265 (0xC1, b'i') => 'ì',
266 (0xC1, b'o') => 'ò',
267 (0xC1, b'u') => 'ù',
268 (0xC2, b'A') => 'Á',
269 (0xC2, b'E') => 'É',
270 (0xC2, b'I') => 'Í',
271 (0xC2, b'O') => 'Ó',
272 (0xC2, b'U') => 'Ú',
273 (0xC2, b'Y') => 'Ý',
274 (0xC2, b'a') => 'á',
275 (0xC2, b'e') => 'é',
276 (0xC2, b'i') => 'í',
277 (0xC2, b'o') => 'ó',
278 (0xC2, b'u') => 'ú',
279 (0xC2, b'y') => 'ý',
280 (0xC2, b'C') => 'Ć',
281 (0xC2, b'c') => 'ć',
282 (0xC2, b'L') => 'Ĺ',
283 (0xC2, b'l') => 'ĺ',
284 (0xC2, b'N') => 'Ń',
285 (0xC2, b'n') => 'ń',
286 (0xC2, b'R') => 'Ŕ',
287 (0xC2, b'r') => 'ŕ',
288 (0xC2, b'S') => 'Ś',
289 (0xC2, b's') => 'ś',
290 (0xC2, b'Z') => 'Ź',
291 (0xC2, b'z') => 'ź',
292 (0xC3, b'A') => 'Â',
293 (0xC3, b'E') => 'Ê',
294 (0xC3, b'I') => 'Î',
295 (0xC3, b'O') => 'Ô',
296 (0xC3, b'U') => 'Û',
297 (0xC3, b'a') => 'â',
298 (0xC3, b'e') => 'ê',
299 (0xC3, b'i') => 'î',
300 (0xC3, b'o') => 'ô',
301 (0xC3, b'u') => 'û',
302 (0xC4, b'A') => 'Ã',
303 (0xC4, b'N') => 'Ñ',
304 (0xC4, b'O') => 'Õ',
305 (0xC4, b'a') => 'ã',
306 (0xC4, b'n') => 'ñ',
307 (0xC4, b'o') => 'õ',
308 (0xC4, b'I') => 'Ĩ',
309 (0xC4, b'i') => 'ĩ',
310 (0xC4, b'U') => 'Ũ',
311 (0xC4, b'u') => 'ũ',
312 (0xC5, b'A') => 'Ā',
314 (0xC5, b'a') => 'ā',
315 (0xC5, b'E') => 'Ē',
316 (0xC5, b'e') => 'ē',
317 (0xC5, b'I') => 'Ī',
318 (0xC5, b'i') => 'ī',
319 (0xC5, b'O') => 'Ō',
320 (0xC5, b'o') => 'ō',
321 (0xC5, b'U') => 'Ū',
322 (0xC5, b'u') => 'ū',
323 (0xC6, b'A') => 'Ă',
325 (0xC6, b'a') => 'ă',
326 (0xC6, b'G') => 'Ğ',
327 (0xC6, b'g') => 'ğ',
328 (0xC6, b'U') => 'Ŭ',
329 (0xC6, b'u') => 'ŭ',
330 (0xC7, b'C') => 'Ċ',
332 (0xC7, b'c') => 'ċ',
333 (0xC7, b'E') => 'Ė',
334 (0xC7, b'e') => 'ė',
335 (0xC7, b'G') => 'Ġ',
336 (0xC7, b'g') => 'ġ',
337 (0xC7, b'I') => 'İ',
338 (0xC7, b'Z') => 'Ż',
339 (0xC7, b'z') => 'ż',
340 (0xC8, b'A') => 'Ä',
341 (0xC8, b'E') => 'Ë',
342 (0xC8, b'I') => 'Ï',
343 (0xC8, b'O') => 'Ö',
344 (0xC8, b'U') => 'Ü',
345 (0xC8, b'Y') => 'Ÿ',
346 (0xC8, b'a') => 'ä',
347 (0xC8, b'e') => 'ë',
348 (0xC8, b'i') => 'ï',
349 (0xC8, b'o') => 'ö',
350 (0xC8, b'u') => 'ü',
351 (0xC8, b'y') => 'ÿ',
352 (0xCA, b'A') => 'Å',
354 (0xCA, b'a') => 'å',
355 (0xCA, b'U') => 'Ů',
356 (0xCA, b'u') => 'ů',
357 (0xCB, b'C') => 'Ç',
358 (0xCB, b'c') => 'ç',
359 (0xCB, b'G') => 'Ģ',
360 (0xCB, b'g') => 'ģ',
361 (0xCB, b'K') => 'Ķ',
362 (0xCB, b'k') => 'ķ',
363 (0xCB, b'L') => 'Ļ',
364 (0xCB, b'l') => 'ļ',
365 (0xCB, b'N') => 'Ņ',
366 (0xCB, b'n') => 'ņ',
367 (0xCB, b'R') => 'Ŗ',
368 (0xCB, b'r') => 'ŗ',
369 (0xCB, b'S') => 'Ş',
370 (0xCB, b's') => 'ş',
371 (0xCB, b'T') => 'Ţ',
372 (0xCB, b't') => 'ţ',
373 (0xCD, b'O') => 'Ő',
375 (0xCD, b'o') => 'ő',
376 (0xCD, b'U') => 'Ű',
377 (0xCD, b'u') => 'ű',
378 (0xCE, b'A') => 'Ą',
380 (0xCE, b'a') => 'ą',
381 (0xCE, b'E') => 'Ę',
382 (0xCE, b'e') => 'ę',
383 (0xCE, b'I') => 'Į',
384 (0xCE, b'i') => 'į',
385 (0xCE, b'U') => 'Ų',
386 (0xCE, b'u') => 'ų',
387 (0xCF, b'C') => 'Č',
389 (0xCF, b'c') => 'č',
390 (0xCF, b'D') => 'Ď',
391 (0xCF, b'd') => 'ď',
392 (0xCF, b'E') => 'Ě',
393 (0xCF, b'e') => 'ě',
394 (0xCF, b'L') => 'Ľ',
395 (0xCF, b'l') => 'ľ',
396 (0xCF, b'N') => 'Ň',
397 (0xCF, b'n') => 'ň',
398 (0xCF, b'R') => 'Ř',
399 (0xCF, b'r') => 'ř',
400 (0xCF, b'S') => 'Š',
401 (0xCF, b's') => 'š',
402 (0xCF, b'T') => 'Ť',
403 (0xCF, b't') => 'ť',
404 (0xCF, b'Z') => 'Ž',
405 (0xCF, b'z') => 'ž',
406 _ => return None,
407 })
408}
409
410fn decode_iso_8859(n: u8, bytes: &[u8]) -> String {
411 use encoding_rs::*;
412 let encoding: &'static Encoding = match n {
413 2 => ISO_8859_2,
414 3 => ISO_8859_3,
415 4 => ISO_8859_4,
416 5 => ISO_8859_5,
417 6 => ISO_8859_6,
418 7 => ISO_8859_7,
419 8 => ISO_8859_8,
420 9 => WINDOWS_1254,
421 10 => ISO_8859_10,
422 11 => WINDOWS_874,
423 13 => ISO_8859_13,
424 14 => ISO_8859_14,
425 15 => ISO_8859_15,
426 _ => return bytes.iter().map(|&b| b as char).collect(),
427 };
428 let (cow, _, _) = encoding.decode(bytes);
429 cow.into_owned()
430}
431
432fn decode_with(encoding: &'static encoding_rs::Encoding, bytes: &[u8]) -> String {
433 let (cow, _, _) = encoding.decode(bytes);
434 cow.into_owned()
435}
436
437fn decode_ucs2_be(bytes: &[u8]) -> String {
438 let code_units: Vec<u16> = bytes
439 .chunks_exact(2)
440 .map(|pair| u16::from_be_bytes([pair[0], pair[1]]))
441 .collect();
442 String::from_utf16_lossy(&code_units)
443}
444
445#[cfg(test)]
446mod tests {
447 use super::*;
448
449 #[test]
450 fn decode_empty_input_returns_empty_string() {
451 assert_eq!(decode_dvb_string(&[]), "");
452 }
453
454 #[test]
455 fn decode_plain_ascii_is_borrowed() {
456 let cow = decode(b"HELLO");
457 assert!(matches!(cow, Cow::Borrowed(_)));
458 assert_eq!(cow, "HELLO");
459 }
460
461 #[test]
462 fn decode_iso6937_latin_accent_chars() {
463 assert_eq!(decode_dvb_string(&[0x00, 0xC2, b'A']), "Á");
464 assert_eq!(decode_dvb_string(&[0x00, 0xC1, b'e']), "è");
465 assert_eq!(decode_dvb_string(&[0x00, 0xC8, b'o']), "ö");
466 }
467
468 #[test]
469 fn decode_selector_0x01_yields_iso8859_5_cyrillic() {
470 let s = decode_dvb_string(&[0x01, 0xB0, 0xB1]);
471 assert!(s.chars().all(|c| c != '\u{FFFD}'), "got: {s:?}");
472 assert!(!s.is_empty());
473 }
474
475 #[test]
476 fn decode_selector_0x10_extended_yields_iso8859_nn() {
477 let s = decode_dvb_string(&[0x10, 0x00, 0x09, b'A', b'B']);
478 assert_eq!(s, "AB");
479 }
480
481 #[test]
482 fn decode_selector_0x11_ucs2_be() {
483 let s = decode_dvb_string(&[0x11, 0x00, 0x41, 0x00, 0x42]);
484 assert_eq!(s, "AB");
485 }
486
487 #[test]
488 fn decode_selector_0x15_utf8_passthrough() {
489 let s = decode_dvb_string(&[0x15, 0xC3, 0xA9, 0xC3, 0xA9]);
490 assert_eq!(s, "éé");
491 }
492
493 #[test]
494 fn decode_control_chars_stripped_linefeed_becomes_space() {
495 let s = decode_dvb_string(b"A\x01B\nC");
496 assert_eq!(s, "AB C");
497 }
498
499 #[test]
500 fn emphasis_on_off_markers_stripped_per_annex_a2() {
501 let s = decode_dvb_string(&[0x00, b'A', 0x86, b'B', 0x87, b'C']);
504 assert_eq!(s, "ABC");
505 }
506
507 #[test]
508 fn decode_annex_a2_crlf_0x8a_becomes_space() {
509 let s = decode_dvb_string(&[0x00, b'A', 0x8A, b'B']);
511 assert_eq!(s, "A B");
512 }
513
514 #[test]
515 fn decode_selector_0x12_ksx1001_euc_kr() {
516 assert_eq!(decode_dvb_string(&[0x12, 0xB0, 0xA1]), "가");
518 }
519
520 #[test]
521 fn decode_selector_0x13_gb2312() {
522 assert_eq!(decode_dvb_string(&[0x13, 0xC4, 0xE3]), "你");
524 }
525
526 #[test]
527 fn decode_selector_0x14_big5() {
528 assert_eq!(decode_dvb_string(&[0x14, 0xA4, 0xA4]), "中");
530 }
531
532 #[test]
536 fn decode_selector_0x13_gbk_trail_byte_in_c1_range() {
537 assert_eq!(decode_dvb_string(&[0x13, 0x81, 0x80]), "亐");
538 }
539
540 #[test]
544 fn two_byte_control_codes_filtered() {
545 assert_eq!(decode_dvb_string(&[0x13, 0xAB, 0xCD]), " ");
546 assert_eq!(decode_dvb_string(&[0x13, 0xAB, 0xC3]), "");
547 }
548
549 #[test]
552 fn decode_selector_0x1f_encoding_type_id() {
553 let s = decode_dvb_string(&[0x1F, 0x01, 0x41, 0x42]);
554 assert_eq!(s.chars().count(), 2);
555 assert!(s.chars().all(|c| c == '\u{FFFD}'));
556 }
557
558 #[test]
560 fn reserved_selector_0x08_is_unsupported() {
561 let s = decode_dvb_string(&[0x08, 0x41, 0x42]);
562 assert!(s.chars().all(|c| c == '\u{FFFD}'));
563 assert_eq!(s.chars().count(), 2);
564 }
565
566 #[test]
567 fn unknown_selector_returns_replacement_characters() {
568 let s = decode_dvb_string(&[0x16, 0xAA, 0xBB, 0xCC]);
570 assert_eq!(s.chars().count(), 3);
571 assert!(s.chars().all(|c| c == '\u{FFFD}'));
572 }
573
574 #[test]
579 fn figure_a1_gr_area_single_byte_mappings() {
580 let pins: &[(u8, char)] = &[
581 (0xA0, '\u{00A0}'), (0xA1, '¡'),
583 (0xA2, '¢'),
584 (0xA3, '£'),
585 (0xA4, '\u{20AC}'), (0xA5, '¥'),
587 (0xA7, '§'),
588 (0xA8, '\u{00A4}'), (0xA9, '\u{2018}'), (0xAA, '\u{201C}'), (0xAB, '«'),
592 (0xAC, '\u{2190}'), (0xAD, '\u{2191}'), (0xAE, '\u{2192}'), (0xAF, '\u{2193}'), (0xB0, '°'),
597 (0xB1, '±'),
598 (0xB2, '²'),
599 (0xB3, '³'),
600 (0xB4, '\u{00D7}'), (0xB5, 'µ'),
602 (0xB6, '¶'),
603 (0xB7, '·'),
604 (0xB8, '\u{00F7}'), (0xB9, '\u{2019}'), (0xBA, '\u{201D}'), (0xBB, '»'),
608 (0xBC, '¼'),
609 (0xBD, '½'),
610 (0xBE, '¾'),
611 (0xBF, '¿'),
612 (0xD0, '\u{2015}'), (0xD1, '¹'),
614 (0xD2, '®'),
615 (0xD3, '©'),
616 (0xD4, '\u{2122}'), (0xD5, '\u{266A}'), (0xD6, '¬'),
619 (0xD7, '\u{00A6}'), (0xDC, '\u{215B}'), (0xDD, '\u{215C}'), (0xDE, '\u{215D}'), (0xDF, '\u{215E}'), (0xE0, '\u{2126}'), (0xE1, 'Æ'),
626 (0xE2, '\u{0110}'), (0xE3, 'ª'),
628 (0xE4, '\u{0126}'), (0xE6, '\u{0132}'), (0xE7, '\u{013F}'), (0xE8, '\u{0141}'), (0xE9, 'Ø'),
633 (0xEA, '\u{0152}'), (0xEB, 'º'),
635 (0xEC, 'Þ'),
636 (0xED, '\u{0166}'), (0xEE, '\u{014A}'), (0xEF, '\u{0149}'), (0xF0, '\u{0138}'), (0xF1, 'æ'),
641 (0xF2, '\u{0111}'), (0xF3, 'ð'),
643 (0xF4, '\u{0127}'), (0xF5, '\u{0131}'), (0xF6, '\u{0133}'), (0xF7, '\u{0140}'), (0xF8, '\u{0142}'), (0xF9, 'ø'),
649 (0xFA, '\u{0153}'), (0xFB, 'ß'),
651 (0xFC, '\u{00FE}'), (0xFD, '\u{0167}'), (0xFE, '\u{014B}'), (0xFF, '\u{00AD}'), ];
656 for &(byte, want) in pins {
657 let got = decode_dvb_string(&[0x00, byte]);
658 assert_eq!(
659 got,
660 want.to_string(),
661 "byte {byte:#04x}: want {want:?} (U+{:04X}), got {got:?}",
662 want as u32
663 );
664 }
665 }
666
667 #[test]
669 fn figure_a1_undefined_positions_are_replacement() {
670 for byte in [0xA6u8, 0xD8, 0xD9, 0xDA, 0xDB, 0xE5] {
671 let got = decode_dvb_string(&[0x00, byte]);
672 assert_eq!(got, "\u{FFFD}", "byte {byte:#04x} should be U+FFFD");
673 }
674 }
675
676 #[test]
678 fn figure_a1_combining_precomposed() {
679 assert_eq!(decode_dvb_string(&[0x00, 0xCA, b'a']), "å"); assert_eq!(decode_dvb_string(&[0x00, 0xCA, b'A']), "Å");
681 assert_eq!(decode_dvb_string(&[0x00, 0xCF, b's']), "š"); assert_eq!(decode_dvb_string(&[0x00, 0xCF, b'Z']), "Ž");
683 assert_eq!(decode_dvb_string(&[0x00, 0xCE, b'e']), "ę"); assert_eq!(decode_dvb_string(&[0x00, 0xCD, b'o']), "ő"); assert_eq!(decode_dvb_string(&[0x00, 0xC7, b'z']), "ż"); assert_eq!(decode_dvb_string(&[0x00, 0xC5, b'a']), "ā"); assert_eq!(decode_dvb_string(&[0x00, 0xC6, b'g']), "ğ"); }
689
690 #[test]
693 fn figure_a1_combining_fallback_emits_base_plus_mark() {
694 assert_eq!(decode_dvb_string(&[0x00, 0xC5, b'x']), "x\u{0304}");
695 }
696
697 #[test]
700 fn figure_a1_combining_undefined_or_dangling_prefix() {
701 assert_eq!(decode_dvb_string(&[0x00, 0xC0, b'a']), "\u{FFFD}a");
702 assert_eq!(decode_dvb_string(&[0x00, 0xC9, b'a']), "\u{FFFD}a");
703 assert_eq!(decode_dvb_string(&[0x00, 0xCC, b'a']), "\u{FFFD}a");
704 assert_eq!(decode_dvb_string(&[0x00, 0xC2]), "\u{FFFD}");
705 }
706}