scroll.server/crates/service-rpc/src/base64.rs
WiseDev d25a6de423 move all crates into crates/, glob members
pure move, no code touched. readme and protocol.md paths fixed up.
2026-08-23 08:15:45 +03:00

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"))
}
}