1use crate::{SB, SC, SNB, SCNB, CRMAP, CCMAP, CNMAP, CBMAP};
2
3macro_rules! index_of_and_char_at {
4 ($rcnb:ident, $chars:expr, $index:expr) => {
5 {
6 let char = $chars.get($index);
7 if char.is_none() {
8 Some(&0usize)
9 } else {
10 $rcnb.get(char.unwrap())
11 }
12 }
13 };
14}
15
16fn decode_byte(chars: &[char]) -> u8 {
17 let mut nb = false;
18 let idx = (|| Some([index_of_and_char_at!(CRMAP, chars, 0)?, index_of_and_char_at!(CCMAP, chars, 1)?]))()
19 .or_else(|| {
20 nb = true;
21 (|| Some([index_of_and_char_at!(CNMAP, chars, 0)?, index_of_and_char_at!(CBMAP, chars, 1)?]))()
22 })
23 .expect("not rc/nb");
24 let result = if nb { idx[0] * SB as usize + idx[1] } else { idx[0] * SC as usize + idx[1] };
25 if result > 0x7E {
26 panic!("rc/nb overflow")
27 }
28
29 (if nb { result | 0x80 } else { result }) as u8
30}
31
32fn decode_short(chars: &[char]) -> usize {
33 let reverse = !CRMAP.contains_key(&chars[0]);
34 let what_is_idx = |order: &[usize; 4]| Some([
35 index_of_and_char_at!(CRMAP, chars, order[0])?,
36 index_of_and_char_at!(CCMAP, chars, order[1])?,
37 index_of_and_char_at!(CNMAP, chars, order[2])?,
38 index_of_and_char_at!(CBMAP, chars, order[3])?
39 ]);
40
41 let idx = if !reverse {
42 what_is_idx(&[0, 1, 2, 3])
43 } else {
44 what_is_idx(&[2, 3, 0, 1])
45 }.expect("not rcnb");
46
47 let result = idx[0] * SCNB as usize + idx[1] * SNB as usize + idx[2] * SB as usize + idx[3];
48
49 if result > 0x7FFF {
50 panic!("rcnb overflow")
51 }
52
53 if reverse { result | 0x8000 } else { result }
54}
55
56pub fn decode(s: &str) -> String {
57 let len = s.chars().count();
58 if (len & 1) != 0 {
59 panic!("invalid length")
60 }
61 let mut v = Vec::with_capacity(len / 2);
62
63 let mut i = 0;
64 let vc = s.chars().collect::<Vec<char>>();
65 while i < (len >> 2) {
66 let start = i * 4;
67 let short = decode_short(&vc[start..start + 4]);
68 v.push((short >> 8) as u8);
69 v.push((short & 0xFF) as u8);
70
71 i += 1;
72 }
73 if (len & 2) != 0 {
74 v.push(decode_byte(&vc[len-2..]))
75 }
76
77 unsafe { String::from_utf8_unchecked(v) }
78}