diff --git a/utils/src/encoding/base16.rs b/utils/src/encoding/base16.rs new file mode 100644 index 0000000..df07123 --- /dev/null +++ b/utils/src/encoding/base16.rs @@ -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"); + } +} diff --git a/utils/src/encoding/base32.rs b/utils/src/encoding/base32.rs new file mode 100644 index 0000000..c8450b0 --- /dev/null +++ b/utils/src/encoding/base32.rs @@ -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======"); + } +} diff --git a/utils/src/encoding/base64.rs b/utils/src/encoding/base64.rs new file mode 100644 index 0000000..85d340e --- /dev/null +++ b/utils/src/encoding/base64.rs @@ -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"); + } +} diff --git a/utils/src/encoding/mod.rs b/utils/src/encoding/mod.rs new file mode 100644 index 0000000..7658ba2 --- /dev/null +++ b/utils/src/encoding/mod.rs @@ -0,0 +1,3 @@ +pub mod base64; +pub mod base32; +pub mod base16; \ No newline at end of file diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 6f17b22..55df7ff 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -1,3 +1,4 @@ pub mod args; pub mod commands; pub mod registry; +pub mod encoding; \ No newline at end of file