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