49 lines
1.9 KiB
Rust
49 lines
1.9 KiB
Rust
const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
pub fn encode(input: &[u8]) -> String {
|
|
let mut out = String::with_capacity(input.len().div_ceil(3) * 4);
|
|
for chunk in input.chunks(3) {
|
|
let b0 = chunk[0] as u32;
|
|
let b1 = chunk.get(1).copied().unwrap_or(0) as u32;
|
|
let b2 = chunk.get(2).copied().unwrap_or(0) as u32;
|
|
let triple = (b0 << 16) | (b1 << 8) | b2;
|
|
out.push(ALPHABET[(triple >> 18) as usize & 0x3F] as char);
|
|
out.push(ALPHABET[(triple >> 12) as usize & 0x3F] as char);
|
|
out.push(if chunk.len() > 1 {
|
|
ALPHABET[(triple >> 6) as usize & 0x3F] as char
|
|
} else {
|
|
'='
|
|
});
|
|
out.push(if chunk.len() > 2 {
|
|
ALPHABET[triple as usize & 0x3F] as char
|
|
} else {
|
|
'='
|
|
});
|
|
}
|
|
out
|
|
}
|
|
pub fn decode(input: &str) -> Option<Vec<u8>> {
|
|
let bytes: Vec<u8> = input.bytes().filter(|byte| *byte != b'=').collect();
|
|
let mut out = Vec::with_capacity(bytes.len() * 3 / 4);
|
|
let mut accumulator: u32 = 0;
|
|
let mut bits: u32 = 0;
|
|
for byte in bytes {
|
|
let value = ALPHABET.iter().position(|candidate| *candidate == byte)? as u32;
|
|
accumulator = (accumulator << 6) | value;
|
|
bits += 6;
|
|
if bits >= 8 {
|
|
bits -= 8;
|
|
out.push((accumulator >> bits) as u8);
|
|
}
|
|
}
|
|
Some(out)
|
|
}
|
|
pub mod serde_bytes {
|
|
use serde::{Deserialize, Deserializer, Serializer};
|
|
pub fn serialize<S: Serializer>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
|
|
serializer.serialize_str(&super::encode(value))
|
|
}
|
|
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
|
let text = String::deserialize(deserializer)?;
|
|
super::decode(&text).ok_or_else(|| serde::de::Error::custom("invalid base64 payload"))
|
|
}
|
|
}
|