base_d/
encoding.rs

1use crate::alphabet::Alphabet;
2use num_traits::Zero;
3use num_integer::Integer;
4
5/// Errors that can occur during decoding.
6#[derive(Debug, PartialEq, Eq)]
7pub enum DecodeError {
8    /// The input contains a character not in the alphabet
9    InvalidCharacter(char),
10    /// The input string is empty
11    EmptyInput,
12    /// The padding is malformed or incorrect
13    InvalidPadding,
14}
15
16impl std::fmt::Display for DecodeError {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            DecodeError::InvalidCharacter(c) => write!(f, "Invalid character in input: {}", c),
20            DecodeError::EmptyInput => write!(f, "Cannot decode empty input"),
21            DecodeError::InvalidPadding => write!(f, "Invalid padding"),
22        }
23    }
24}
25
26impl std::error::Error for DecodeError {}
27
28pub fn encode(data: &[u8], alphabet: &Alphabet) -> String {
29    if data.is_empty() {
30        return String::new();
31    }
32    
33    // Count leading zeros for efficient handling
34    let leading_zeros = data.iter().take_while(|&&b| b == 0).count();
35    
36    // If all zeros, return early
37    if leading_zeros == data.len() {
38        return alphabet.encode_digit(0).unwrap().to_string().repeat(data.len());
39    }
40    
41    let base = alphabet.base();
42    let mut num = num_bigint::BigUint::from_bytes_be(&data[leading_zeros..]);
43    
44    // Pre-allocate result vector with estimated capacity
45    let max_digits = ((data.len() - leading_zeros) * 8 * 1000) / (base as f64).log2() as usize / 1000 + 1;
46    let mut result = Vec::with_capacity(max_digits + leading_zeros);
47    
48    let base_big = num_bigint::BigUint::from(base);
49    
50    while !num.is_zero() {
51        let (quotient, remainder) = num.div_rem(&base_big);
52        let digit = remainder.to_u64_digits();
53        let digit_val = if digit.is_empty() { 0 } else { digit[0] as usize };
54        result.push(alphabet.encode_digit(digit_val).unwrap());
55        num = quotient;
56    }
57    
58    // Add leading zeros
59    for _ in 0..leading_zeros {
60        result.push(alphabet.encode_digit(0).unwrap());
61    }
62    
63    result.reverse();
64    result.into_iter().collect()
65}
66
67pub fn decode(encoded: &str, alphabet: &Alphabet) -> Result<Vec<u8>, DecodeError> {
68    if encoded.is_empty() {
69        return Err(DecodeError::EmptyInput);
70    }
71    
72    let base = alphabet.base();
73    let mut num = num_bigint::BigUint::from(0u8);
74    let base_big = num_bigint::BigUint::from(base);
75    
76    // Collect chars once for better cache performance
77    let chars: Vec<char> = encoded.chars().collect();
78    let mut leading_zeros = 0;
79    
80    // Process in chunks for better performance
81    for &c in &chars {
82        let digit = alphabet.decode_char(c)
83            .ok_or(DecodeError::InvalidCharacter(c))?;
84        
85        if num.is_zero() && digit == 0 {
86            leading_zeros += 1;
87        } else {
88            num *= &base_big;
89            num += num_bigint::BigUint::from(digit);
90        }
91    }
92    
93    // Handle all-zero case
94    if num.is_zero() && leading_zeros > 0 {
95        return Ok(vec![0u8; leading_zeros]);
96    }
97    
98    let bytes = num.to_bytes_be();
99    
100    // Construct result with pre-allocated capacity
101    let mut result = Vec::with_capacity(leading_zeros + bytes.len());
102    result.resize(leading_zeros, 0u8);
103    result.extend_from_slice(&bytes);
104    
105    Ok(result)
106}