1 use std::collections::HashMap;
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         // _s
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 
print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize)46 fn print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
47     println!("#[cfg_attr(rustfmt, rustfmt_skip)]");
48     println!(
49         "{:width$}pub const {}: &'static [u8; 64] = &[",
50         "",
51         const_name,
52         width = indent_depth
53     );
54 
55     for (i, b) in alphabet.iter().enumerate() {
56         println!(
57             "{:width$}{}, // input {} (0x{:X}) => '{}' (0x{:X})",
58             "",
59             b,
60             i,
61             i,
62             String::from_utf8(vec![*b as u8]).unwrap(),
63             b,
64             width = indent_depth + 4
65         );
66     }
67 
68     println!("{:width$}];", "", width = indent_depth);
69 }
70 
print_decode_table(alphabet: &[u8], const_name: &str, indent_depth: usize)71 fn print_decode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
72     // map of alphabet bytes to 6-bit morsels
73     let mut input_to_morsel = HashMap::<u8, u8>::new();
74 
75     // standard base64 alphabet bytes, in order
76     for (morsel, ascii_byte) in alphabet.iter().enumerate() {
77         // truncation cast is fine here
78         let _ = input_to_morsel.insert(*ascii_byte, morsel as u8);
79     }
80 
81     println!("#[cfg_attr(rustfmt, rustfmt_skip)]");
82     println!(
83         "{:width$}pub const {}: &'static [u8; 256] = &[",
84         "",
85         const_name,
86         width = indent_depth
87     );
88     for ascii_byte in 0..256 {
89         let (value, comment) = match input_to_morsel.get(&(ascii_byte as u8)) {
90             None => (
91                 "INVALID_VALUE".to_string(),
92                 format!("input {} (0x{:X})", ascii_byte, ascii_byte),
93             ),
94             Some(v) => (
95                 format!("{}", *v),
96                 format!(
97                     "input {} (0x{:X} char '{}') => {} (0x{:X})",
98                     ascii_byte,
99                     ascii_byte,
100                     String::from_utf8(vec![ascii_byte as u8]).unwrap(),
101                     *v,
102                     *v
103                 ),
104             ),
105         };
106 
107         println!(
108             "{:width$}{}, // {}",
109             "",
110             value,
111             comment,
112             width = indent_depth + 4
113         );
114     }
115     println!("{:width$}];", "", width = indent_depth);
116 }
117