1const BASE64_ALPHABET: &[u8; 64] =
2 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
4
5pub fn base64_encode(input: &[u8]) -> String {
7 let mut out = String::with_capacity(input.len().div_ceil(3) * 4);
8 let mut chunks = input.chunks_exact(3);
9 for chunk in chunks.by_ref() {
10 let bits = ((chunk[0] as u32) << 16) | ((chunk[1] as u32) << 8) | chunk[2] as u32;
11 out.push(BASE64_ALPHABET[((bits >> 18) & 0x3f) as usize] as char);
12 out.push(BASE64_ALPHABET[((bits >> 12) & 0x3f) as usize] as char);
13 out.push(BASE64_ALPHABET[((bits >> 6) & 0x3f) as usize] as char);
14 out.push(BASE64_ALPHABET[(bits & 0x3f) as usize] as char);
15 }
16 match chunks.remainder() {
17 [] => {}
18 [first] => {
19 let bits = (*first as u32) << 16;
20 out.push(BASE64_ALPHABET[((bits >> 18) & 0x3f) as usize] as char);
21 out.push(BASE64_ALPHABET[((bits >> 12) & 0x3f) as usize] as char);
22 out.push_str("==");
23 }
24 [first, second] => {
25 let bits = ((*first as u32) << 16) | ((*second as u32) << 8);
26 out.push(BASE64_ALPHABET[((bits >> 18) & 0x3f) as usize] as char);
27 out.push(BASE64_ALPHABET[((bits >> 12) & 0x3f) as usize] as char);
28 out.push(BASE64_ALPHABET[((bits >> 6) & 0x3f) as usize] as char);
29 out.push('=');
30 }
31 _ => unreachable!(),
32 }
33 out
34}
35
36pub fn json_escape(input: &str) -> String {
38 let mut out = String::with_capacity(input.len());
39 for character in input.chars() {
40 match character {
41 '"' => out.push_str("\\\""),
42 '\\' => out.push_str("\\\\"),
43 '\n' => out.push_str("\\n"),
44 '\r' => out.push_str("\\r"),
45 '\t' => out.push_str("\\t"),
46 control if (control as u32) < 0x20 => {
47 let byte = control as usize;
48 out.push_str("\\u00");
49 out.push(HEX_DIGITS[byte >> 4] as char);
50 out.push(HEX_DIGITS[byte & 0x0f] as char);
51 }
52 other => out.push(other),
53 }
54 }
55 out
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn base64_standard_vectors() {
64 let vectors: &[(&[u8], &str)] = &[
65 (b"", ""),
66 (b"f", "Zg=="),
67 (b"fo", "Zm8="),
68 (b"foo", "Zm9v"),
69 (b"foob", "Zm9vYg=="),
70 (b"fooba", "Zm9vYmE="),
71 (b"foobar", "Zm9vYmFy"),
72 (&[0xfb, 0xff, 0xbf], "+/+/"),
73 ];
74
75 for (input, expected) in vectors {
76 assert_eq!(base64_encode(input), *expected);
77 }
78 }
79
80 #[test]
81 fn json_escape_vectors() {
82 let vectors = [
83 ("", ""),
84 ("plain / text", "plain / text"),
85 ("\"\\\n\r\t", "\\\"\\\\\\n\\r\\t"),
86 ("\0\u{0008}\u{000c}\u{001f}", "\\u0000\\u0008\\u000c\\u001f"),
87 ("olá 🧪", "olá 🧪"),
88 ];
89
90 for (input, expected) in vectors {
91 assert_eq!(json_escape(input), expected);
92 }
93 }
94}