use std::io::{ErrorKind, Read, Result, Write};
const LOWER_ALPHABETS: &[u8] = b"0123456789abcdef";
const HIGHER_ALPHABETS: &[u8] = b"ghjkmnpqrstvwxyz";
enum LeadNybbleStatus {
None,
PreviousHigh,
PreviousLow,
}
pub fn azam_encode_write<R: Read, W: Write>(reader: &mut R, writer: &mut W) -> Result<usize> {
let mut byte = [0u8; 1];
let mut count: usize = 0;
reader.read_exact(&mut byte)?;
count += 1;
let mut lead_nybble = if (byte[0] >> 4) > 0 {
LeadNybbleStatus::PreviousHigh
} else if (byte[0] & 0x0fu8) > 0 {
LeadNybbleStatus::PreviousLow
} else {
LeadNybbleStatus::None
};
let mut prev_byte = byte[0];
loop {
match reader.read_exact(&mut byte) {
Ok(()) => {
count += 1;
match lead_nybble {
LeadNybbleStatus::None => {
lead_nybble = if (byte[0] >> 4) > 0 {
LeadNybbleStatus::PreviousHigh
} else if (byte[0] & 0x0fu8) > 0 {
LeadNybbleStatus::PreviousLow
} else {
LeadNybbleStatus::None
};
}
LeadNybbleStatus::PreviousHigh => {
let high_nybble = prev_byte >> 4;
let low_nybble = prev_byte & 0x0fu8;
writer.write_all(&[HIGHER_ALPHABETS[high_nybble as usize]])?;
writer.write_all(&[HIGHER_ALPHABETS[low_nybble as usize]])?;
}
LeadNybbleStatus::PreviousLow => {
let low_nybble = prev_byte & 0x0fu8;
writer.write_all(&[HIGHER_ALPHABETS[low_nybble as usize]])?;
lead_nybble = LeadNybbleStatus::PreviousHigh;
}
}
prev_byte = byte[0];
}
Err(err) => {
if err.kind() == ErrorKind::UnexpectedEof {
break;
} else {
return Err(err);
}
}
}
}
match lead_nybble {
LeadNybbleStatus::None => {
if count > 0 {
writer.write_all(&[LOWER_ALPHABETS[0]])?;
}
}
LeadNybbleStatus::PreviousHigh => {
let high_nybble = prev_byte >> 4;
let low_nybble = prev_byte & 0x0fu8;
writer.write_all(&[HIGHER_ALPHABETS[high_nybble as usize]])?;
writer.write_all(&[LOWER_ALPHABETS[low_nybble as usize]])?;
}
LeadNybbleStatus::PreviousLow => {
let low_nybble = prev_byte & 0x0fu8;
writer.write_all(&[LOWER_ALPHABETS[low_nybble as usize]])?;
}
}
Ok(count)
}
pub fn azam_encode_bytes_to_bytes(value: Vec<u8>) -> Vec<u8> {
let mut encoded = Vec::<u8>::new();
azam_encode_write(&mut value.as_slice(), &mut encoded).unwrap();
encoded
}
pub fn azam_encode_bytes(value: Vec<u8>) -> String {
String::from_utf8(azam_encode_bytes_to_bytes(value)).unwrap()
}
pub fn azam_encode_bytes_vec_to_bytes(value: Vec<Vec<u8>>) -> Vec<u8> {
let mut encoded = Vec::<u8>::new();
for vec in value {
encoded.append(&mut azam_encode_bytes_to_bytes(vec.clone()));
}
encoded
}
pub fn azam_encode_bytes_vec(value: Vec<Vec<u8>>) -> String {
String::from_utf8(azam_encode_bytes_vec_to_bytes(value)).unwrap()
}
pub trait AzamEncode {
fn azam_encode_write<W: Write>(&self, writer: &mut W) -> Result<usize>;
fn azam_encode(&self) -> String;
}
macro_rules! azam_encode_uint_impl {
($t:ty) => {
impl AzamEncode for $t {
fn azam_encode_write<W: Write>(&self, writer: &mut W) -> Result<usize> {
$crate::encode::azam_encode_write(&mut self.to_be_bytes().as_ref(), writer)
}
fn azam_encode(&self) -> String {
let mut bytes = Vec::<u8>::new();
$crate::encode::AzamEncode::azam_encode_write(self, &mut bytes).unwrap();
String::from_utf8(bytes).unwrap()
}
}
};
}
azam_encode_uint_impl!(u8);
azam_encode_uint_impl!(u16);
azam_encode_uint_impl!(u32);
azam_encode_uint_impl!(u64);
azam_encode_uint_impl!(u128);
#[macro_export]
macro_rules! azam_encode {
() => {};
($value:expr) => {{
$crate::encode::AzamEncode::azam_encode(&$value)
}};
($($values:expr),*) => {{
let mut bytes = Vec::<u8>::new();
$(
$crate::encode::AzamEncode::azam_encode_write(&$values, &mut bytes).unwrap();
)*
String::from_utf8(bytes).unwrap()
}};
}
#[cfg(test)]
mod tests {
use crate::encode::*;
#[test]
fn test_azam_encode_example() {
assert_eq!("xytxvyyf", 0xdeadbeefu32.azam_encode());
assert_eq!("h5", 0x15u8.azam_encode());
assert_eq!("wgg1", 0xc001u16.azam_encode());
}
#[test]
fn test_azam_encode_uints() {
assert_eq!("0", 0x00u8.azam_encode());
assert_eq!("1", 0x01u8.azam_encode());
assert_eq!("f", 0x0fu8.azam_encode());
assert_eq!("h0", 0x10u8.azam_encode());
assert_eq!("zf", 0xffu8.azam_encode());
assert_eq!("0", 0x0000u32.azam_encode());
assert_eq!("1", 0x0001u32.azam_encode());
assert_eq!("f", 0x000fu32.azam_encode());
assert_eq!("h0", 0x0010u32.azam_encode());
assert_eq!("zf", 0x00ffu32.azam_encode());
assert_eq!("hgg0", 0x1000u32.azam_encode());
}
#[test]
fn test_azam_encode_macro() {
assert_eq!("zf", azam_encode!(0xffu8));
assert_eq!("zzzf", azam_encode!(0xffffu16));
assert_eq!(
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzf",
azam_encode!(0xffffffffffffffffffffffffffffffffu128)
);
assert_eq!(
"zfzzzfzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzf",
azam_encode!(0xffu8, 0xffffu16, 0xffffffffffffffffffffffffffffffffu128)
);
}
}