use core::ptr::write;
use crate::shared::*;
use super::alphabet::{BITS, WIDTH_DEC, WIDTH_ENC};
const U8_MASK_MID_2: u8 = 0b01111100;
const U8_MASK_MID_1: u8 = 0b00111110;
#[inline]
pub const fn capacity_encode(a: &[u8]) -> usize {
(a.len() * WIDTH_ENC + (WIDTH_DEC - 1)) / WIDTH_DEC
}
#[inline]
const fn rem_enc(rem: usize) -> usize {
match rem {
4 => 7,
3 => 5,
2 => 4,
1 => 2,
_ => 0,
}
}
pub fn encode(enc: &'static [u8; BITS], a: &[u8]) -> String {
let len_dec = a.len();
let rem = len_dec % WIDTH_DEC;
let max = len_dec / WIDTH_DEC;
let p_max = max * WIDTH_ENC;
let rem_enc = rem_enc(rem);
let mut b = Vec::<u8>::with_capacity(p_max + rem_enc);
encode_inner(enc, a, &mut b, max, 0, p_max, rem, rem_enc);
unsafe { String::from_utf8_unchecked(b) }
}
pub fn encode_into(enc: &'static [u8; BITS], a: &[u8], b: &mut Vec<u8>) {
let len_dec = a.len();
let rem = len_dec % WIDTH_DEC;
let max = len_dec / WIDTH_DEC;
let len_enc = b.len();
let p_max = len_enc + max * WIDTH_ENC;
let rem_enc = rem_enc(rem);
assert!(
b.capacity() >= p_max + rem_enc,
"Missing capacity for encoding"
);
encode_inner(enc, a, b, max, len_enc, p_max, rem, rem_enc);
}
#[rustfmt::skip]
#[inline(always)]
fn encode_inner(enc: &'static [u8; BITS], a: &[u8], b: &mut Vec<u8>, max: usize, len_enc: usize, p_max: usize, rem: usize, rem_enc: usize) {
for i in 0..max {
unsafe {
let c = i * WIDTH_DEC;
let p = len_enc + i * WIDTH_ENC;
let end = b.as_mut_ptr().add(p);
write(end , enc[( (a[c ] & U8_MASK_TOP_5) >> 3 ) as usize]);
write(end.add(1), enc[(((a[c ] & U8_MASK_BOT_3) << 2) | ((a[c+1] & U8_MASK_TOP_2) >> 6)) as usize]);
write(end.add(2), enc[( (a[c+1] & U8_MASK_MID_1) >> 1 ) as usize]);
write(end.add(3), enc[(((a[c+1] & U8_MASK_BOT_1) << 4) | ((a[c+2] & U8_MASK_TOP_4) >> 4)) as usize]);
write(end.add(4), enc[(((a[c+2] & U8_MASK_BOT_4) << 1) | ((a[c+3] & U8_MASK_TOP_1) >> 7)) as usize]);
write(end.add(5), enc[( (a[c+3] & U8_MASK_MID_2) >> 2 ) as usize]);
write(end.add(6), enc[(((a[c+3] & U8_MASK_BOT_2) << 3) | ((a[c+4] & U8_MASK_TOP_3) >> 5)) as usize]);
write(end.add(7), enc[( a[c+4] & U8_MASK_BOT_5 ) as usize]);
b.set_len(p + WIDTH_ENC);
}
}
match rem {
4 => unsafe {
let c = max * WIDTH_DEC;
let end = b.as_mut_ptr().add(p_max);
write(end , enc[( (a[c ] & U8_MASK_TOP_5) >> 3 ) as usize]);
write(end.add(1), enc[(((a[c ] & U8_MASK_BOT_3) << 2) | ((a[c+1] & U8_MASK_TOP_2) >> 6)) as usize]);
write(end.add(2), enc[( (a[c+1] & U8_MASK_MID_1) >> 1 ) as usize]);
write(end.add(3), enc[(((a[c+1] & U8_MASK_BOT_1) << 4) | ((a[c+2] & U8_MASK_TOP_4) >> 4)) as usize]);
write(end.add(4), enc[(((a[c+2] & U8_MASK_BOT_4) << 1) | ((a[c+3] & U8_MASK_TOP_1) >> 7)) as usize]);
write(end.add(5), enc[( (a[c+3] & U8_MASK_MID_2) >> 2 ) as usize]);
write(end.add(6), enc[(((a[c+3] & U8_MASK_BOT_2) << 3) ) as usize]);
b.set_len(p_max + rem_enc);
}
3 => unsafe {
let c = max * WIDTH_DEC;
let end = b.as_mut_ptr().add(p_max);
write(end , enc[( (a[c ] & U8_MASK_TOP_5) >> 3 ) as usize]);
write(end.add(1), enc[(((a[c ] & U8_MASK_BOT_3) << 2) | ((a[c+1] & U8_MASK_TOP_2) >> 6)) as usize]);
write(end.add(2), enc[( (a[c+1] & U8_MASK_MID_1) >> 1 ) as usize]);
write(end.add(3), enc[(((a[c+1] & U8_MASK_BOT_1) << 4) | ((a[c+2] & U8_MASK_TOP_4) >> 4)) as usize]);
write(end.add(4), enc[(((a[c+2] & U8_MASK_BOT_4) << 1) ) as usize]);
b.set_len(p_max + rem_enc);
}
2 => unsafe {
let c = max * WIDTH_DEC;
let end = b.as_mut_ptr().add(p_max);
write(end , enc[( (a[c ] & U8_MASK_TOP_5) >> 3 ) as usize]);
write(end.add(1), enc[(((a[c ] & U8_MASK_BOT_3) << 2) | ((a[c+1] & U8_MASK_TOP_2) >> 6)) as usize]);
write(end.add(2), enc[( (a[c+1] & U8_MASK_MID_1) >> 1 ) as usize]);
write(end.add(3), enc[(((a[c+1] & U8_MASK_BOT_1) << 4) ) as usize]);
b.set_len(p_max + rem_enc);
}
1 => unsafe {
let c = max * WIDTH_DEC;
let end = b.as_mut_ptr().add(p_max);
write(end , enc[( (a[c ] & U8_MASK_TOP_5) >> 3 ) as usize]);
write(end.add(1), enc[(((a[c ] & U8_MASK_BOT_3) << 2) ) as usize]);
b.set_len(p_max + rem_enc);
}
_ => {}
}
}