use crate::{
DecodeError, FORBIDDEN_BYTE,
util::{BITS, Digit, DoubleDigit},
};
pub(crate) fn write_bytes_be(num: &[Digit], out: &mut [u8]) {
if num.is_empty() || (num.len() == 1 && unsafe { *num.get_unchecked(0) } == 0) {
return;
}
let len = out.len();
let num_len = num.len();
let last = unsafe { *num.get_unchecked(num_len - 1) };
let last_bytes = 8 - (last.leading_zeros() / 8) as usize;
for (i, &d) in num.iter().enumerate() {
let mut val = d;
if i == num_len - 1 {
for j in 0..last_bytes {
let idx = len - 1 - (i * 8 + j);
unsafe {
*out.get_unchecked_mut(idx) = val as u8;
}
val >>= 8;
}
} else {
let bytes = val.to_be_bytes();
let start_idx = len - (i + 1) * 8;
unsafe {
let target = out.get_unchecked_mut(start_idx..start_idx + 8);
target.copy_from_slice(&bytes);
}
}
}
}
pub(crate) fn mul_add255(num: &mut Vec<Digit>, add_val: Digit) {
let mut carry = add_val as DoubleDigit;
for d in num.iter_mut() {
carry += *d as DoubleDigit * 255;
*d = carry as Digit;
carry >>= BITS;
}
if carry != 0 {
num.push(carry as Digit);
}
}
pub fn decode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
let data = data.as_ref();
if data.is_empty() {
return Ok(Vec::new());
}
let trailing_zeros = data.iter().rev().take_while(|&&b| b == 0).count();
let core_data = &data[..data.len() - trailing_zeros];
if core_data.is_empty() {
return Ok(vec![0; trailing_zeros]);
}
let mut num: Vec<Digit> = Vec::with_capacity(core_data.len().div_ceil(8));
num.push(0);
for &byte in core_data.iter().rev() {
let digit = match byte {
FORBIDDEN_BYTE => return Err(DecodeError::InvalidByte(FORBIDDEN_BYTE)),
255 => FORBIDDEN_BYTE,
b => b,
};
mul_add255(&mut num, digit as Digit);
}
let num_len = num.len();
let last = unsafe { *num.get_unchecked(num_len - 1) };
let last_bytes = 8 - (last.leading_zeros() / 8) as usize;
let len = (num_len - 1) * 8 + last_bytes;
let total_len = trailing_zeros + len;
let mut result = vec![0; total_len];
write_bytes_be(&num, unsafe { result.get_unchecked_mut(trailing_zeros..) });
Ok(result)
}