pub extern crate num_bigint;
pub extern crate num_traits;
use lazy_static::lazy_static;
use num_bigint::{BigUint, ToBigUint};
use num_traits::{ToPrimitive, Zero};
use std::{convert::TryInto, error::Error, usize};
const BASE: usize = 62;
const ALPHABET_STR: &str =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
lazy_static! {
static ref ALPHABET: [char; BASE] = ALPHABET_STR
.chars()
.collect::<Vec<char>>()
.try_into()
.unwrap();
}
pub fn encode_data(data: &[u8]) -> String {
let mut data = data.to_owned();
data.insert(0, 0x01);
let num = BigUint::from_bytes_be(&data);
encode_num(&num)
}
pub fn encode_num<T: ToBigUint>(num: &T) -> String {
let base = BigUint::from(BASE);
let zero = BigUint::zero();
let mut num = num
.to_biguint()
.expect("Failed to convert `num` to `BigUint`");
if num == zero {
return "0".to_owned();
}
let mut digits = vec![];
while num > zero {
let rem = (&num % &base)
.to_usize()
.expect("THIS SHOULD NEVER HAPPEN: Failed converting to `usize`");
digits.push(ALPHABET[rem]);
num /= &base;
}
digits.iter().rev().collect()
}
pub fn decode_data(inp: &str) -> Result<Vec<u8>, Box<dyn Error>> {
let num = decode_num(inp)?;
let data = num.to_bytes_be();
if data.len() == 0 || data[0] != 0x01 {
return Err(Box::from("Encoded data is invalid: Encoded data must begin with a `0x01` magic byte."));
}
let data = data[1..].to_vec();
Ok(data)
}
pub fn decode_data_forgiving(inp: &str) -> Result<Vec<u8>, Box<dyn Error>> {
let num = decode_num(inp)?;
let data = num.to_bytes_be();
if data.len() == 0 || data[0] != 0x01 {
return Ok(data);
}
let data = data[1..].to_vec();
Ok(data)
}
pub fn decode_num(inp: &str) -> Result<BigUint, Box<dyn Error>> {
let base = BigUint::from(BASE);
let mut num = BigUint::zero();
for digit in inp.chars() {
let rem = digit_to_num(digit)?;
num *= &base;
num += rem;
}
Ok(num)
}
fn digit_to_num(digit: char) -> Result<u32, String> {
let num = match digit {
'0'..='9' => 0 + (digit as u32) - ('0' as u32),
'A'..='Z' => 10 + (digit as u32) - ('A' as u32),
'a'..='z' => 36 + (digit as u32) - ('a' as u32),
_ => return Err(format!("Invalid character '{}'", &digit)),
};
Ok(num)
}
#[cfg(test)]
mod tests {
mod encode {
use num_traits::FromPrimitive;
use super::super::*;
#[test]
fn encode_single_byte() {
assert_eq!(encode_data(&vec![0xAA]), "6s")
}
#[test]
fn encode_multiple_bytes() {
let data = vec![
0x49, 0x20, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65,
0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x64, 0x61, 0x74, 0x61,
0x62, 0x6c, 0x6f, 0x62, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x79,
0x20, 0x67, 0x69, 0x72, 0x6c, 0x66, 0x72, 0x69, 0x65, 0x6e,
0x64, 0x20, 0x53, 0x61, 0x73, 0x73, 0x69, 0x2c, 0x20, 0x77,
0x68, 0x6f, 0x6d, 0x20, 0x49, 0x20, 0x6c, 0x6f, 0x76, 0x65,
0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6e,
0x20, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20,
0x3c, 0x33,
];
assert_eq!(
encode_data(&data),
"2dijkpweCkKuJQVIavyqgkzOPiqWWiLHD0KzzlsZDQnGjtD6s6znCYYROVDlomz7lrxiMgpUEKZ7MnmwWnlpEmUNcJ4WW4wEJKATPQ9Fg5oqnVD"
);
}
#[test]
fn encode_leading_zeroes() {
assert_eq!(encode_data(&vec![0x00, 0x00, 0x10]), "18OWW")
}
#[test]
fn encode_trailing_zeroes() {
assert_eq!(encode_data(&vec![0x01, 0x00, 0x00]), "18fZI")
}
#[test]
fn encode_numbers() {
assert_eq!(encode_num(&0), "0");
assert_eq!(encode_num(&5), "5");
assert_eq!(encode_num(&9), "9");
assert_eq!(encode_num(&10), "A");
assert_eq!(encode_num(&35), "Z");
assert_eq!(encode_num(&36), "a");
assert_eq!(encode_num(&61), "z");
assert_eq!(encode_num(&62), "10");
assert_eq!(encode_num(&1337), "LZ");
assert_eq!(encode_num(&10.0), "A");
assert_eq!(encode_num(&10_u32), "A");
assert_eq!(encode_num(&10_usize), "A");
assert_eq!(encode_num(&BigUint::from_i32(10).unwrap()), "A");
}
}
mod decode {
use num_traits::FromPrimitive;
use super::super::*;
#[test]
fn decode_single_byte() {
assert_eq!(decode_data("6s").unwrap(), vec![0xAA])
}
#[test]
fn decode_multiple_bytes() {
let encoded = "2dijkpweCkKuJQVIavyqgkzOPiqWWiLHD0KzzlsZDQnGjtD6s6znCYYROVDlomz7lrxiMgpUEKZ7MnmwWnlpEmUNcJ4WW4wEJKATPQ9Fg5oqnVD";
let data = vec![
0x49, 0x20, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65,
0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x64, 0x61, 0x74, 0x61,
0x62, 0x6c, 0x6f, 0x62, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x79,
0x20, 0x67, 0x69, 0x72, 0x6c, 0x66, 0x72, 0x69, 0x65, 0x6e,
0x64, 0x20, 0x53, 0x61, 0x73, 0x73, 0x69, 0x2c, 0x20, 0x77,
0x68, 0x6f, 0x6d, 0x20, 0x49, 0x20, 0x6c, 0x6f, 0x76, 0x65,
0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6e,
0x20, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20,
0x3c, 0x33,
];
assert_eq!(decode_data(&encoded).unwrap(), data);
}
#[test]
fn decode_leading_zeroes() {
let encoded = "18OWW";
assert_eq!(decode_data(&encoded).unwrap(), vec![0x00, 0x00, 0x10])
}
#[test]
fn decode_trailing_zeroes() {
assert_eq!(decode_data("18fZI").unwrap(), vec![0x01, 0x00, 0x00])
}
#[test]
fn decode_numbers() {
assert_eq!(
decode_num(&"0").unwrap(),
BigUint::from_i32(0).unwrap()
);
assert_eq!(
decode_num(&"5").unwrap(),
BigUint::from_i32(5).unwrap()
);
assert_eq!(
decode_num(&"9").unwrap(),
BigUint::from_i32(9).unwrap()
);
assert_eq!(
decode_num(&"A").unwrap(),
BigUint::from_i32(10).unwrap()
);
assert_eq!(
decode_num(&"Z").unwrap(),
BigUint::from_i32(35).unwrap()
);
assert_eq!(
decode_num(&"a").unwrap(),
BigUint::from_i32(36).unwrap()
);
assert_eq!(
decode_num(&"z").unwrap(),
BigUint::from_i32(61).unwrap()
);
assert_eq!(
decode_num(&"10").unwrap(),
BigUint::from_i32(62).unwrap()
);
assert_eq!(
decode_num(&"LZ").unwrap(),
BigUint::from_i32(1337).unwrap()
);
}
}
}