1pub mod base64 {
2 use std::{borrow::Cow, sync::LazyLock};
3
4 use base64_simd::{Base64 as Raw, Error, STANDARD};
5 use regex::Regex;
6
7 pub struct Base64(Raw);
8
9 impl Base64 {
10 pub const fn new() -> Self {
11 Self(STANDARD)
12 }
13
14 pub fn encode_to_string<D: AsRef<[u8]>>(&self, data: D) -> String {
15 self.0.encode_to_string(data)
16 }
17
18 pub fn decode_to_vec<D: AsRef<[u8]>>(&self, data: D) -> Result<Vec<u8>, Error> {
19 self.0.decode_to_vec(data)
20 }
21 }
22
23 impl Default for Base64 {
24 fn default() -> Self {
25 Self::new()
26 }
27 }
28
29 static BASE64: Base64 = Base64::new();
30
31 pub fn encode_to_string<D: AsRef<[u8]>>(data: D) -> String {
32 BASE64.0.encode_to_string(data)
33 }
34
35 pub fn decode_to_vec<D: AsRef<[u8]>>(data: D) -> Result<Vec<u8>, Error> {
36 BASE64.0.decode_to_vec(data)
37 }
38
39 static INVALID_BASE64_RE: LazyLock<Regex> =
40 LazyLock::new(|| Regex::new(r"[^+/0-9A-Za-z-_]").expect("Invalid RegExp"));
41
42 pub fn clean_base64(value: &str) -> Option<Cow<'_, str>> {
46 let value = value.split('=').next()?;
47 let value = value.trim();
48 let value = INVALID_BASE64_RE.replace_all(value, "");
49 if value.len() < 2 {
50 return Some(Cow::from(""));
51 }
52 let value = value.into_owned();
53 let len = value.len();
54 let remainder = len % 4;
55 if remainder == 0 {
56 return Some(Cow::from(value));
57 }
58 let pad_len = 4 - remainder;
59 if pad_len == 1 {
60 return Some(Cow::from(value + "="));
61 }
62 if pad_len == 2 {
63 return Some(Cow::from(value + "=="));
64 }
65 Some(Cow::from(value[..len - remainder].to_owned()))
75 }
76}
77
78pub use base64::{clean_base64, decode_to_vec, encode_to_string};