1use bytes::Bytes;
4use serde::{de, Deserialize, Deserializer, Serializer};
5
6const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7
8pub fn encode(data: &[u8]) -> String {
14 let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
15 for chunk in data.chunks(3) {
16 let b = [
17 chunk[0],
18 chunk.get(1).copied().unwrap_or(0),
19 chunk.get(2).copied().unwrap_or(0),
20 ];
21 let n = (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]);
22 for i in 0..4 {
23 if i <= chunk.len() {
24 out.push(char::from(ALPHABET[((n >> (18 - 6 * i)) & 0x3f) as usize]));
25 } else {
26 out.push('=');
27 }
28 }
29 }
30 out
31}
32
33pub fn decode(text: &str) -> Result<Vec<u8>, &'static str> {
40 fn value(c: u8) -> Result<u32, &'static str> {
41 match c {
42 b'A'..=b'Z' => Ok(u32::from(c - b'A')),
43 b'a'..=b'z' => Ok(u32::from(c - b'a') + 26),
44 b'0'..=b'9' => Ok(u32::from(c - b'0') + 52),
45 b'+' => Ok(62),
46 b'/' => Ok(63),
47 _ => Err("invalid base64 character"),
48 }
49 }
50 let trimmed = text.trim_end_matches('=');
51 if text.len() - trimmed.len() > 2 {
52 return Err("too much base64 padding");
53 }
54 let bytes = trimmed.as_bytes();
55 if bytes.len() % 4 == 1 {
56 return Err("invalid base64 length");
57 }
58 let mut out = Vec::with_capacity(bytes.len() * 3 / 4);
59 for chunk in bytes.chunks(4) {
60 let mut n = 0u32;
61 for (i, &c) in chunk.iter().enumerate() {
62 n |= value(c)? << (18 - 6 * i);
63 }
64 let [_, b0, b1, b2] = n.to_be_bytes();
65 out.push(b0);
66 if chunk.len() > 2 {
67 out.push(b1);
68 }
69 if chunk.len() > 3 {
70 out.push(b2);
71 }
72 }
73 Ok(out)
74}
75
76pub(crate) mod serde_bytes {
78 use super::*;
79
80 pub(crate) fn serialize<S: Serializer>(data: &Bytes, s: S) -> Result<S::Ok, S::Error> {
81 s.serialize_str(&encode(data))
82 }
83
84 pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Bytes, D::Error> {
85 let text = <std::borrow::Cow<'de, str>>::deserialize(d)?;
86 decode(&text).map(Bytes::from).map_err(de::Error::custom)
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn round_trips_every_length() {
96 for len in 0..40usize {
97 let data: Vec<u8> = (0..len).map(|i| (i * 37 % 256) as u8).collect();
98 assert_eq!(decode(&encode(&data)).unwrap(), data, "length {len}");
99 }
100 assert_eq!(encode(b"Man"), "TWFu");
101 assert_eq!(encode(b"Ma"), "TWE=");
102 assert_eq!(encode(b"M"), "TQ==");
103 assert_eq!(decode("TQ").unwrap(), b"M");
104 assert!(decode("T").is_err());
105 }
106}