feat: add encodings to boxutils, no dependencies! :D
This commit is contained in:
parent
d8bc6b620e
commit
60956e3831
5 changed files with 222 additions and 0 deletions
52
utils/src/encoding/base16.rs
Normal file
52
utils/src/encoding/base16.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
const BASE16_ALPHABET: &[u8] = b"0123456789ABCDEF";
|
||||||
|
|
||||||
|
fn encode(to_encode: String) -> String {
|
||||||
|
let bytes = to_encode.as_bytes();
|
||||||
|
let mut encoded = String::new();
|
||||||
|
|
||||||
|
for &byte in bytes {
|
||||||
|
encoded.push(BASE16_ALPHABET[(byte >> 4) as usize] as char);
|
||||||
|
encoded.push(BASE16_ALPHABET[(byte & 0x0F) as usize] as char);
|
||||||
|
}
|
||||||
|
encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(to_decode: String) -> String {
|
||||||
|
let bytes = to_decode.as_bytes();
|
||||||
|
|
||||||
|
let mut decoded = Vec::new();
|
||||||
|
for chunk in bytes.chunks(2) {
|
||||||
|
let high = BASE16_ALPHABET.iter().position(|&c| c == chunk[0]).unwrap() as u8;
|
||||||
|
let low = BASE16_ALPHABET.iter().position(|&c| c == chunk[1]).unwrap() as u8;
|
||||||
|
decoded.push((high << 4) | low);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::from_utf8(decoded).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode() {
|
||||||
|
let encoded = encode("Hello, world!".to_string());
|
||||||
|
assert_eq!(encoded, "48656C6C6F2C20776F726C6421");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode() {
|
||||||
|
let decoded = decode("48656C6C6F2C20776F726C6421".into());
|
||||||
|
assert_eq!(decoded, "Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_using_rfc() {
|
||||||
|
assert_eq!(encode("".into()), "");
|
||||||
|
assert_eq!(encode("f".into()), "66");
|
||||||
|
assert_eq!(encode("fo".into()), "666F");
|
||||||
|
assert_eq!(encode("foo".into()), "666F6F");
|
||||||
|
assert_eq!(encode("foob".into()), "666F6F62");
|
||||||
|
assert_eq!(encode("fooba".into()), "666F6F6261");
|
||||||
|
assert_eq!(encode("foobar".into()), "666F6F626172");
|
||||||
|
}
|
||||||
|
}
|
||||||
75
utils/src/encoding/base32.rs
Normal file
75
utils/src/encoding/base32.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
const BASE32_ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||||
|
|
||||||
|
fn encode(to_encode: String) -> String {
|
||||||
|
let bytes = to_encode.as_bytes();
|
||||||
|
let mut encoded = String::new();
|
||||||
|
let mut bits = 0u32;
|
||||||
|
let mut bit_count = 0;
|
||||||
|
|
||||||
|
for &byte in bytes {
|
||||||
|
bits = (bits << 8) | byte as u32;
|
||||||
|
bit_count += 8;
|
||||||
|
while bit_count >= 5 {
|
||||||
|
let index = (bits >> (bit_count - 5)) & 0b11111;
|
||||||
|
encoded.push(BASE32_ALPHABET[index as usize] as char);
|
||||||
|
bit_count -= 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bit_count > 0 {
|
||||||
|
let index = (bits << (5 - bit_count)) & 0b11111;
|
||||||
|
encoded.push(BASE32_ALPHABET[index as usize] as char);
|
||||||
|
}
|
||||||
|
|
||||||
|
while encoded.len() % 8 != 0 {
|
||||||
|
encoded.push('=');
|
||||||
|
}
|
||||||
|
encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(to_decode: String) -> String {
|
||||||
|
let bytes = to_decode.as_bytes();
|
||||||
|
let mut decoded = Vec::new();
|
||||||
|
let mut bits = 0u32;
|
||||||
|
let mut bit_count = 0;
|
||||||
|
|
||||||
|
for &b in bytes.iter().filter(|&&b| b != b'=') {
|
||||||
|
if let Some(pos) = BASE32_ALPHABET.iter().position(|&c| c == b) {
|
||||||
|
bits = (bits << 5) | pos as u32;
|
||||||
|
bit_count += 5;
|
||||||
|
if bit_count >= 8 {
|
||||||
|
decoded.push((bits >> (bit_count - 8)) as u8);
|
||||||
|
bit_count -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String::from_utf8(decoded).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode() {
|
||||||
|
let encoded = encode("Hello, world!".to_string());
|
||||||
|
assert_eq!(encoded, "JBSWY3DPFQQHO33SNRSCC===");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode() {
|
||||||
|
let decoded = decode("JBSWY3DPFQQHO33SNRSCC===".into());
|
||||||
|
assert_eq!(decoded, "Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_using_rfc() {
|
||||||
|
assert_eq!(encode("".into()), "");
|
||||||
|
assert_eq!(encode("f".into()), "MY======");
|
||||||
|
assert_eq!(encode("fo".into()), "MZXQ====");
|
||||||
|
assert_eq!(encode("foo".into()), "MZXW6===");
|
||||||
|
assert_eq!(encode("foob".into()), "MZXW6YQ=");
|
||||||
|
assert_eq!(encode("fooba".into()), "MZXW6YTB");
|
||||||
|
assert_eq!(encode("foobar".into()), "MZXW6YTBOI======");
|
||||||
|
}
|
||||||
|
}
|
||||||
91
utils/src/encoding/base64.rs
Normal file
91
utils/src/encoding/base64.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
const BASE64_ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
fn encode(to_encode: String) -> String {
|
||||||
|
let bytes = to_encode.as_bytes();
|
||||||
|
let mut encoded = String::new();
|
||||||
|
let mut chunks = bytes.chunks_exact(3);
|
||||||
|
|
||||||
|
for chunk in &mut chunks {
|
||||||
|
let b1 = chunk[0] as u32;
|
||||||
|
let b2 = chunk[1] as u32;
|
||||||
|
let b3 = chunk[2] as u32;
|
||||||
|
|
||||||
|
encoded.push(BASE64_ALPHABET[(b1 >> 2) as usize] as char);
|
||||||
|
encoded.push(BASE64_ALPHABET[((b1 & 0b11) << 4 | (b2 >> 4)) as usize] as char);
|
||||||
|
encoded.push(BASE64_ALPHABET[((b2 & 0b1111) << 2 | (b3 >> 6)) as usize] as char);
|
||||||
|
encoded.push(BASE64_ALPHABET[(b3 & 0b111111) as usize] as char);
|
||||||
|
}
|
||||||
|
|
||||||
|
let remainder = chunks.remainder();
|
||||||
|
if !remainder.is_empty() {
|
||||||
|
let b1 = remainder[0] as u32;
|
||||||
|
encoded.push(BASE64_ALPHABET[(b1 >> 2) as usize] as char);
|
||||||
|
if remainder.len() == 2 {
|
||||||
|
let b2 = remainder[1] as u32;
|
||||||
|
encoded.push(BASE64_ALPHABET[((b1 & 0b11) << 4 | (b2 >> 4)) as usize] as char);
|
||||||
|
encoded.push(BASE64_ALPHABET[((b2 & 0b1111) << 2) as usize] as char);
|
||||||
|
encoded.push('=');
|
||||||
|
} else {
|
||||||
|
encoded.push(BASE64_ALPHABET[((b1 & 0b11) << 4) as usize] as char);
|
||||||
|
encoded.push_str("==");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(to_decode: String) -> String {
|
||||||
|
let bytes = to_decode.as_bytes();
|
||||||
|
|
||||||
|
let mut decoded = Vec::new();
|
||||||
|
let mut buffer = [0u8; 4];
|
||||||
|
let mut index = 0;
|
||||||
|
|
||||||
|
for &b in bytes.iter().filter(|&&b| b != b'=') {
|
||||||
|
if let Some(pos) = BASE64_ALPHABET.iter().position(|&c| c == b) {
|
||||||
|
buffer[index] = pos as u8;
|
||||||
|
index += 1;
|
||||||
|
if index == 4 {
|
||||||
|
decoded.push(buffer[0] << 2 | buffer[1] >> 4);
|
||||||
|
decoded.push(buffer[1] << 4 | buffer[2] >> 2);
|
||||||
|
decoded.push(buffer[2] << 6 | buffer[3]);
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index > 1 {
|
||||||
|
decoded.push(buffer[0] << 2 | buffer[1] >> 4);
|
||||||
|
}
|
||||||
|
if index > 2 {
|
||||||
|
decoded.push(buffer[1] << 4 | buffer[2] >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
String::from_utf8(decoded).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode() {
|
||||||
|
let encoded = encode("Hello, world!".to_string());
|
||||||
|
assert_eq!(encoded, "SGVsbG8sIHdvcmxkIQ==");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode() {
|
||||||
|
let decoded = decode("SGVsbG8sIHdvcmxkIQ==".into());
|
||||||
|
assert_eq!(decoded, "Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_using_rfc() {
|
||||||
|
assert_eq!(encode("".into()), "");
|
||||||
|
assert_eq!(encode("f".into()), "Zg==");
|
||||||
|
assert_eq!(encode("fo".into()), "Zm8=");
|
||||||
|
assert_eq!(encode("foo".into()), "Zm9v");
|
||||||
|
assert_eq!(encode("foob".into()), "Zm9vYg==");
|
||||||
|
assert_eq!(encode("fooba".into()), "Zm9vYmE=");
|
||||||
|
assert_eq!(encode("foobar".into()), "Zm9vYmFy");
|
||||||
|
}
|
||||||
|
}
|
||||||
3
utils/src/encoding/mod.rs
Normal file
3
utils/src/encoding/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod base64;
|
||||||
|
pub mod base32;
|
||||||
|
pub mod base16;
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
|
pub mod encoding;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue