#![allow(dead_code)]
const BASE64_TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
pub fn percent_decode(input: &str) -> String {
let mut result = Vec::with_capacity(input.len());
let bytes = input.as_bytes();
let mut index = 0;
while index < bytes.len() {
if bytes[index] == b'%'
&& index + 2 < bytes.len()
&& let (Some(high), Some(low)) = (hex_val(bytes[index + 1]), hex_val(bytes[index + 2]))
{
result.push(high << 4 | low);
index += 3;
continue;
}
result.push(bytes[index]);
index += 1;
}
String::from_utf8(result).unwrap_or_else(|_| input.to_string())
}
pub fn percent_encode(input: &str) -> String {
let mut result = String::with_capacity(input.len());
for byte in input.bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
result.push(byte as char);
}
_ => result.push_str(&format!("%{byte:02X}")),
}
}
result
}
pub fn html_escape(input: &str) -> String {
input
.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
pub fn base64_decode(input: &str) -> Option<Vec<u8>> {
let input = input.trim();
if input.is_empty() {
return Some(Vec::new());
}
let mut output = Vec::with_capacity(input.len() * 3 / 4);
let mut buffer = 0u32;
let mut bits = 0u32;
for &byte in input.as_bytes() {
if byte == b'=' {
break;
}
let value = match BASE64_TABLE.iter().position(|&candidate| candidate == byte) {
Some(value) => value as u32,
None => {
if byte == b'\n' || byte == b'\r' || byte == b' ' {
continue;
}
return None;
}
};
buffer = (buffer << 6) | value;
bits += 6;
if bits >= 8 {
bits -= 8;
output.push((buffer >> bits) as u8);
buffer &= (1 << bits) - 1;
}
}
Some(output)
}
fn hex_val(byte: u8) -> Option<u8> {
match byte {
b'0'..=b'9' => Some(byte - b'0'),
b'a'..=b'f' => Some(byte - b'a' + 10),
b'A'..=b'F' => Some(byte - b'A' + 10),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn percent_round_trip() {
assert_eq!(percent_decode("%2F"), "/");
assert_eq!(percent_encode("hello world"), "hello%20world");
}
#[test]
fn html_escaping() {
assert_eq!(
html_escape("<script>alert('xss')</script>"),
"<script>alert('xss')</script>"
);
}
#[test]
fn base64_decoding() {
assert_eq!(base64_decode("SGVsbG8="), Some(b"Hello".to_vec()));
}
}