1pub mod engine {
2 pub mod general_purpose {
3 pub struct Standard;
4
5 impl Standard {
6 pub fn encode<T: AsRef<[u8]>>(&self, bytes: T) -> String {
8 super::super::encode(bytes.as_ref())
9 }
10
11 pub fn decode<T: AsRef<str>>(&self, s: T) -> Result<Vec<u8>, super::super::DecodeError> {
13 super::super::decode(s.as_ref())
14 }
15 }
16
17 pub const STANDARD: Standard = Standard;
18 }
19}
20
21#[derive(Debug)]
22pub struct DecodeError;
23
24impl std::fmt::Display for DecodeError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 write!(f, "Format Base64 tidak valid")
27 }
28}
29
30impl std::error::Error for DecodeError {}
31
32pub fn encode(input: &[u8]) -> String {
34 const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
35 let mut output = String::with_capacity((input.len() + 2) / 3 * 4);
36 for chunk in input.chunks(3) {
37 let b0 = chunk[0] as usize;
38 let b1 = chunk.get(1).map(|&b| b as usize).unwrap_or(0);
39 let b2 = chunk.get(2).map(|&b| b as usize).unwrap_or(0);
40
41 output.push(CHARS[b0 >> 2] as char);
42 output.push(CHARS[((b0 & 3) << 4) | (b1 >> 4)] as char);
43
44 if chunk.len() > 1 {
45 output.push(CHARS[((b1 & 15) << 2) | (b2 >> 6)] as char);
46 } else {
47 output.push('=');
48 }
49
50 if chunk.len() > 2 {
51 output.push(CHARS[b2 & 63] as char);
52 } else {
53 output.push('=');
54 }
55 }
56 output
57}
58
59pub fn decode(input: &str) -> Result<Vec<u8>, DecodeError> {
61 let input = input.trim_end_matches('=');
62 let mut output = Vec::with_capacity(input.len() * 3 / 4);
63 let mut buffer = 0u32;
64 let mut bits = 0;
65
66 for &c in input.as_bytes() {
67 let val = match c {
68 b'A'..=b'Z' => c - b'A',
69 b'a'..=b'z' => c - b'a' + 26,
70 b'0'..=b'9' => c - b'0' + 52,
71 b'+' => 62,
72 b'/' => 63,
73 _ => return Err(DecodeError),
74 } as u32;
75
76 buffer = (buffer << 6) | val;
77 bits += 6;
78
79 if bits >= 8 {
80 bits -= 8;
81 output.push((buffer >> bits) as u8);
82 }
83 }
84 Ok(output)
85}