use crate::binarytext::BinaryText;
use crate::error::BinTxtError;
#[derive(Clone, Debug)]
pub struct Base45 {}
impl Default for Base45 {
fn default() -> Self {
Self::new()
}
}
impl Base45 {
pub fn new() -> Self {
Self {}
}
}
impl BinaryText for Base45 {
fn base(&self) -> usize {
45
}
fn name(&self) -> &str {
"Base45"
}
fn n_bytes_encode(&self) -> usize {
2
}
fn n_bytes_decode(&self) -> usize {
3
}
fn encode_byte(&self, byte: u8) -> Result<u8, BinTxtError> {
const LUT: [u8; 45] = [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D',
b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R',
b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b' ', b'$', b'%', b'*', b'+', b'-',
b'.', b'/', b':',
];
if byte >= 45 {
let msg = format!("Byte {byte} exceeds maximum {}", 45);
return Err(BinTxtError::EncodingErr(msg));
}
Ok(LUT[byte as usize])
}
fn encode_into_vec(&self, input: &[u8], res: &mut Vec<u8>) -> Result<(), BinTxtError> {
const BASE: [u16; 3] = [1u16, 45u16, 45u16 * 45u16];
res.clear();
let iter = input.chunks_exact(2);
let bytes_rem = iter.remainder();
for bytes in iter {
let mut val = (bytes[0] as u16) << 8 | (bytes[1] as u16);
let mut arr = [u8::MAX; 3];
let tmp = val / BASE[2];
arr[2] = tmp as u8;
val -= tmp * BASE[2];
let tmp = val / BASE[1];
arr[1] = tmp as u8;
val -= tmp * BASE[1];
arr[0] = val as u8;
for b in arr {
let b = self.encode_byte(b)?;
res.push(b);
}
}
if !bytes_rem.is_empty() {
let mut val = bytes_rem[0] as u16;
let mut arr = [u8::MAX; 2];
let tmp = val / BASE[1];
arr[1] = tmp as u8;
val -= tmp * BASE[1];
arr[0] = val as u8;
for a in arr {
let b = self.encode_byte(a)?;
res.push(b);
}
}
Ok(())
}
fn decode_byte(&self, byte: u8) -> Result<u8, BinTxtError> {
const LUT: [u8; 96] = [
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 36, 255,
255, 255, 37, 38, 255, 255, 255, 255, 39, 40, 255, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 44, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
];
let b = if byte < 96 { LUT[byte as usize] } else { 255 };
if b < 255 {
Ok(b)
} else {
let errmsg = format!("Invalid byte \"{}\" in Base45 string", byte);
Err(BinTxtError::DecodingErr(errmsg))
}
}
fn decode_into_vec(&self, input: &[u8], res: &mut Vec<u8>) -> Result<(), BinTxtError> {
const BASE: [u16; 3] = [1u16, 45u16, 45u16 * 45u16];
res.clear();
let iter = input.chunks_exact(3);
let bytes_rem = iter.remainder();
for bytes in iter {
let dec = self.decode_byte(bytes[0])?;
let mut val = dec as u16;
let dec = self.decode_byte(bytes[1])?;
val += dec as u16 * BASE[1];
let dec = self.decode_byte(bytes[2])?;
val += dec as u16 * BASE[2];
let b0 = (val >> 8) as u8;
res.push(b0);
let b1 = val as u8;
res.push(b1);
}
if bytes_rem.len() == 2 {
let mut val = 0u16;
let dec = self.decode_byte(bytes_rem[0])?;
val += dec as u16;
let dec = self.decode_byte(bytes_rem[1])?;
val += dec as u16 * BASE[1];
let b = val as u8;
res.push(b);
}
if bytes_rem.len() == 1 {
let errmsg = "Invalid remainder of length one in Base45 string".to_string();
return Err(BinTxtError::DecodingErr(errmsg));
}
Ok(())
}
fn is_decodable(&self, input: &str) -> bool {
for &byte in input.as_bytes() {
match self.decode_byte(byte) {
Ok(_) => {}
Err(_) => {
return false;
}
}
}
true
}
}