Skip to content

Instantly share code, notes, and snippets.

@LiosK
Created November 12, 2022 00:52
Show Gist options
  • Save LiosK/d655130b2f563bba1e35d638bf4bdf90 to your computer and use it in GitHub Desktop.
Save LiosK/d655130b2f563bba1e35d638bf4bdf90 to your computer and use it in GitHub Desktop.
Converts a digit value array in `src_base` to that in `dst_base`
// XXX NOT TESTED
/// Converts a digit value array in `src_base` to that in `dst_base`.
///
/// # Panics
///
/// Panics if:
///
/// - `src_base` is not between 2 and 256, inclusive;
/// - `dst_base` is not between 2 and 256, inclusive; or,
/// - `dst` is too small to store the result.
#[inline]
pub fn convert_base(src: &[u8], src_base: usize, dst: &mut [u8], dst_base: usize) {
assert!((2..=256).contains(&src_base) && (2..=256).contains(&dst_base));
let (src_base, dst_base) = (src_base as u64, dst_base as u64);
let (word_len, word_base) = compute_word_size(src_base, dst_base);
dst.fill(0);
let mut dst_used = dst.len() - 1; // storage to memorize range of `dst` filled
// read `word_len` digits from `src` for each outer loop
for chunk in src.rchunks(word_len).rev() {
let mut carry = chunk.iter().fold(0u64, |acc, e| acc * src_base + *e as u64);
// fill in `dst` from right to left, while carrying up prior result to left
for (i, e) in dst.iter_mut().enumerate().rev() {
carry += *e as u64 * word_base;
*e = (carry % dst_base) as u8;
carry /= dst_base;
// break inner loop when `carry` and remaining `dst` digits are all zero
if carry == 0 && i <= dst_used {
dst_used = i;
break;
}
}
assert_eq!(carry, 0, "too small dst");
}
}
/// Determines the number of `src` digits to read for each outer loop.
const fn compute_word_size(src_base: u64, dst_base: u64) -> (usize, u64) {
let mut word_len = 0;
let mut word_base = 1;
while word_base < u64::MAX / (src_base * dst_base) {
word_len += 1;
word_base *= src_base;
}
(word_len, word_base)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment