binarytext 0.1.2

Binary-to-text encoders / decoders
Documentation
//! Implementation of IntEncoder used for encoding and decoding integers.

use crate::binarytext::{BinaryText, build_decoding_lut};
use crate::error::BinTxtError;

#[derive(Clone, Debug)]
pub struct IntEncoder {
    name: String,
    lut_enc: Vec<u8>,
    lut_dec: [u8; 128],
}

impl IntEncoder {
    /// Returns the Base36 encoder.
    pub fn base36() -> Self {
        let name = "Base36".to_string();
        let lut_enc = vec![
            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',
        ];
        let lut_dec = build_decoding_lut(&lut_enc);
        Self {
            name,
            lut_enc,
            lut_dec,
        }
    }

    /// Returns the Base56 encoder.
    pub fn base56() -> Self {
        let name = "Base56".to_string();
        let lut_enc = vec![
            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'J', b'K', b'L', b'M', b'N', b'P', b'Q', b'R', b'S', b'T', b'U', b'V',
            b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j',
            b'k', b'm', b'n', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z',
        ];
        let lut_dec = build_decoding_lut(&lut_enc);
        Self {
            name,
            lut_enc,
            lut_dec,
        }
    }

    /// Returns the Base58 encoder.
    pub fn base58() -> Self {
        let name = "Base58".to_string();
        let lut_enc = vec![
            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'J', b'K', b'L', b'M', b'N', b'P', b'Q', b'R', b'S', b'T', b'U',
            b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i',
            b'j', b'k', 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',
        ];
        let lut_dec = build_decoding_lut(&lut_enc);
        Self {
            name,
            lut_enc,
            lut_dec,
        }
    }

    /// Returns the Base62 encoder.
    pub fn base62() -> Self {
        let name = "Base62".to_string();
        let lut_enc = vec![
            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'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',
        ];
        let lut_dec = build_decoding_lut(&lut_enc);
        Self {
            name,
            lut_enc,
            lut_dec,
        }
    }
}

impl BinaryText for IntEncoder {
    fn base(&self) -> usize {
        self.lut_enc.len()
    }

    fn name(&self) -> &str {
        self.name.as_str()
    }

    fn n_bytes_encode(&self) -> usize {
        1
    }

    fn n_bytes_decode(&self) -> usize {
        1
    }

    fn encode_byte(&self, byte: u8) -> Result<u8, BinTxtError> {
        let max = self.base() as u8;
        if byte >= max {
            let msg = format!("Byte {byte} exceeds maximum {}", 62);
            return Err(BinTxtError::EncodingErr(msg));
        }
        Ok(self.lut_enc[byte as usize])
    }

    fn encode_into_vec(&self, input: &[u8], res: &mut Vec<u8>) -> Result<(), BinTxtError> {
        if input.len() > 16 {
            let msg = "Unable to encode more than 16 bytes with IntEncoder".to_string();
            return Err(BinTxtError::EncodingErr(msg));
        }
        let mut int = 0u128;
        for (ind, &byte) in input.iter().rev().enumerate() {
            int += (byte as u128) << (ind * 8);
        }
        let enc = self.encode_u128(int)?;
        res.clear();
        for b in enc.bytes() {
            res.push(b);
        }
        Ok(())
    }

    fn decode_byte(&self, byte: u8) -> Result<u8, BinTxtError> {
        let b = if byte < 128 {
            self.lut_dec[byte as usize]
        } else {
            255
        };
        if b < 255 {
            Ok(b)
        } else {
            let errmsg = format!("Invalid byte \"{}\" in {} string", byte, self.name());
            Err(BinTxtError::DecodingErr(errmsg))
        }
    }

    fn decode_into_vec(&self, input: &[u8], res: &mut Vec<u8>) -> Result<(), BinTxtError> {
        res.clear();
        let int = self.decode_u128(input)?;
        let mut start = false;
        for ind_byte in (0..16).rev() {
            let tmp = (int >> (ind_byte * 8)) as u8;
            if tmp > 0 {
                start = true;
            }
            if start {
                res.push(tmp);
            }
        }
        Ok(())
    }

    fn is_decodable(&self, input: &str) -> bool {
        for &byte in input.as_bytes() {
            match self.decode_byte(byte) {
                Ok(_) => {}
                Err(_) => {
                    return false;
                }
            }
        }
        true
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_alphabet() {
        let base56 = IntEncoder::base56();
        for &e in base56.lut_enc.iter() {
            let pos = e as usize;
            assert!(base56.lut_dec[pos] != 255);
        }
        let base58 = IntEncoder::base58();
        for &e in base58.lut_enc.iter() {
            let pos = e as usize;
            assert!(base58.lut_dec[pos] != 255);
        }
        let base62 = IntEncoder::base62();
        for &e in base62.lut_enc.iter() {
            let pos = e as usize;
            assert!(base62.lut_dec[pos] != 255);
        }
    }
}