use num::BigUint;
use num::Integer;
use num::ToPrimitive;
use thiserror::Error;
pub static CHARACTERS: &[u8; 94] = include_bytes!("characters.txt");
pub fn encode(data: &[u8], base: u8) -> String {
assert!(base <= 94, "Base must be less than or equal to 94");
assert!(base >= 2, "Base must be greater than or equal to 2");
let mut num = BigUint::from_bytes_le(data);
let mut out = String::new();
while num > BigUint::from(0u8) {
let (div, rem) = num.div_rem(&BigUint::from(base));
num = div;
out.push(CHARACTERS[rem.to_usize().unwrap()] as char);
}
out
}
#[derive(Error, Debug)]
pub enum DecodeError {
#[error("Invalid character '{c}' at position {position}")]
InvalidCharacter { c: u8, position: usize },
}
pub fn decode(encoded: &str, base: u8) -> Result<Vec<u8>, DecodeError> {
let mut num = BigUint::from(0u8);
let mut power = BigUint::from(1u8);
for (i, c) in encoded.chars().enumerate() {
let index =
CHARACTERS
.iter()
.position(|&x| x == c as u8)
.ok_or(DecodeError::InvalidCharacter {
c: c as u8,
position: i,
})?;
num += BigUint::from(index) * &power;
power *= BigUint::from(base);
}
let out = num.to_bytes_le();
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
const MAX_BASE: u8 = 94;
#[test]
fn test_encode_decode_empty() {
for base in 2..=MAX_BASE {
let data = [0];
let encoded = encode(&data, base);
let decoded = decode(&encoded, base).unwrap();
assert_eq!(decoded, data);
}
}
#[test]
fn test_encode_decode_single_byte() {
for base in 2..=MAX_BASE {
let data = [65]; let encoded = encode(&data, base);
let decoded = decode(&encoded, base).unwrap();
assert_eq!(decoded, data);
}
}
#[test]
fn test_encode_decode_hello_world() {
for base in 2..=MAX_BASE {
let data = b"Hello, World!";
let encoded = encode(data, base);
let decoded = decode(&encoded, base).unwrap();
assert_eq!(decoded, data);
}
}
#[test]
fn test_encode_decode_max_value() {
for base in 2..=MAX_BASE {
let data = [255, 255, 255]; let encoded = encode(&data, base);
let decoded = decode(&encoded, base).unwrap();
assert_eq!(decoded, data);
}
}
#[test]
fn test_encode_decode_large_data() {
for base in 2..=MAX_BASE {
let data = vec![42; 1000]; let encoded = encode(&data, base);
let decoded = decode(&encoded, base).unwrap();
assert_eq!(decoded, data);
}
}
}