1 use std::collections::{HashMap, HashSet};
2 use std::iter::Iterator;
3 
main()4 fn main() {
5     println!("pub const INVALID_VALUE: u8 = 255;");
6 
7     // A-Z
8     let standard_alphabet: Vec<u8> = (0x41..0x5B)
9         // a-z
10         .chain(0x61..0x7B)
11         // 0-9
12         .chain(0x30..0x3A)
13         // +
14         .chain(0x2B..0x2C)
15         // /
16         .chain(0x2F..0x30)
17         .collect();
18     print_encode_table(&standard_alphabet, "STANDARD_ENCODE", 0);
19     print_decode_table(&standard_alphabet, "STANDARD_DECODE", 0);
20 
21     // A-Z
22     let url_alphabet: Vec<u8> = (0x41..0x5B)
23         // a-z
24         .chain(0x61..0x7B)
25         // 0-9
26         .chain(0x30..0x3A)
27         // -
28         .chain(0x2D..0x2E)
29         // _
30         .chain(0x5F..0x60)
31         .collect();
32     print_encode_table(&url_alphabet, "URL_SAFE_ENCODE", 0);
33     print_decode_table(&url_alphabet, "URL_SAFE_DECODE", 0);
34 
35     // ./0123456789
36     let crypt_alphabet: Vec<u8> = (b'.'..(b'9' + 1))
37         // A-Z
38         .chain(b'A'..(b'Z' + 1))
39         // a-z
40         .chain(b'a'..(b'z' + 1))
41         .collect();
42     print_encode_table(&crypt_alphabet, "CRYPT_ENCODE", 0);
43     print_decode_table(&crypt_alphabet, "CRYPT_DECODE", 0);
44 
45     // ./
46     let bcrypt_alphabet: Vec<u8> = (b'.'..(b'/' + 1))
47         // A-Z
48         .chain(b'A'..(b'Z' + 1))
49         // a-z
50         .chain(b'a'..(b'z' + 1))
51         // 0-9
52         .chain(b'0'..(b'9' + 1))
53         .collect();
54     print_encode_table(&bcrypt_alphabet, "BCRYPT_ENCODE", 0);
55     print_decode_table(&bcrypt_alphabet, "BCRYPT_DECODE", 0);
56 
57     // A-Z
58     let imap_alphabet: Vec<u8> = (0x41..0x5B)
59         // a-z
60         .chain(0x61..0x7B)
61         // 0-9
62         .chain(0x30..0x3A)
63         // +
64         .chain(0x2B..0x2C)
65         // ,
66         .chain(0x2C..0x2D)
67         .collect();
68     print_encode_table(&imap_alphabet, "IMAP_MUTF7_ENCODE", 0);
69     print_decode_table(&imap_alphabet, "IMAP_MUTF7_DECODE", 0);
70 
71     // '!' - '-'
72     let binhex_alphabet: Vec<u8> = (0x21..0x2E)
73         // 0-9
74         .chain(0x30..0x3A)
75         // @-N
76         .chain(0x40..0x4F)
77         // P-V
78         .chain(0x50..0x57)
79         // X-[
80         .chain(0x58..0x5C)
81         // `-f
82         .chain(0x60..0x66)
83         // h-m
84         .chain(0x68..0x6E)
85         // p-r
86         .chain(0x70..0x73)
87         .collect();
88     print_encode_table(&binhex_alphabet, "BINHEX_ENCODE", 0);
89     print_decode_table(&binhex_alphabet, "BINHEX_DECODE", 0);
90 }
91 
print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize)92 fn print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
93     check_alphabet(alphabet);
94     println!("#[rustfmt::skip]");
95     println!(
96         "{:width$}pub const {}: &[u8; 64] = &[",
97         "",
98         const_name,
99         width = indent_depth
100     );
101 
102     for (i, b) in alphabet.iter().enumerate() {
103         println!(
104             "{:width$}{}, // input {} (0x{:X}) => '{}' (0x{:X})",
105             "",
106             b,
107             i,
108             i,
109             String::from_utf8(vec![*b as u8]).unwrap(),
110             b,
111             width = indent_depth + 4
112         );
113     }
114 
115     println!("{:width$}];", "", width = indent_depth);
116 }
117 
print_decode_table(alphabet: &[u8], const_name: &str, indent_depth: usize)118 fn print_decode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
119     check_alphabet(alphabet);
120     // map of alphabet bytes to 6-bit morsels
121     let mut input_to_morsel = HashMap::<u8, u8>::new();
122 
123     // standard base64 alphabet bytes, in order
124     for (morsel, ascii_byte) in alphabet.iter().enumerate() {
125         // truncation cast is fine here
126         let _ = input_to_morsel.insert(*ascii_byte, morsel as u8);
127     }
128 
129     println!("#[rustfmt::skip]");
130     println!(
131         "{:width$}pub const {}: &[u8; 256] = &[",
132         "",
133         const_name,
134         width = indent_depth
135     );
136     for ascii_byte in 0..256 {
137         let (value, comment) = match input_to_morsel.get(&(ascii_byte as u8)) {
138             None => (
139                 "INVALID_VALUE".to_string(),
140                 format!("input {} (0x{:X})", ascii_byte, ascii_byte),
141             ),
142             Some(v) => (
143                 format!("{}", *v),
144                 format!(
145                     "input {} (0x{:X} char '{}') => {} (0x{:X})",
146                     ascii_byte,
147                     ascii_byte,
148                     String::from_utf8(vec![ascii_byte as u8]).unwrap(),
149                     *v,
150                     *v
151                 ),
152             ),
153         };
154 
155         println!(
156             "{:width$}{}, // {}",
157             "",
158             value,
159             comment,
160             width = indent_depth + 4
161         );
162     }
163     println!("{:width$}];", "", width = indent_depth);
164 }
165 
check_alphabet(alphabet: &[u8])166 fn check_alphabet(alphabet: &[u8]) {
167     assert_eq!(64, alphabet.len());
168     let mut set: HashSet<u8> = HashSet::new();
169     set.extend(alphabet);
170     assert_eq!(64, set.len());
171 }
172