use core::ops::{BitAnd, BitOr, Shr};
use fearless_simd::{Level, Simd, dispatch, prelude::*, u8x16, u32x4, u32x8, u32x16};
use crate::BitnucError;
pub fn encode(seq: &[u8], ebuf: &mut [u8]) -> Result<(), BitnucError> {
let n_bytes = seq.len().div_ceil(4);
if ebuf.len() < n_bytes {
return Err(BitnucError::EncodingBufferTooSmall {
expected: n_bytes,
actual: ebuf.len(),
});
}
let level = Level::new();
dispatch!(level, simd => encode_inner(simd, seq, ebuf));
Ok(())
}
#[allow(clippy::uninit_vec)]
pub fn encode_resize(seq: &[u8], ebuf: &mut Vec<u8>) {
let n_bytes = seq.len().div_ceil(4);
if ebuf.len() < n_bytes {
ebuf.reserve(n_bytes - ebuf.len());
unsafe {
ebuf.set_len(n_bytes); }
}
let level = Level::new();
dispatch!(level, simd => encode_inner(simd, seq, &mut ebuf[..n_bytes]));
}
const ENCODE_LUT: [u8; 16] = [
0, 0, 1, 0, 3, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
#[inline(always)]
fn encode_inner<S: Simd>(simd: S, seq: &[u8], ebuf: &mut [u8]) {
let lut_block = u8x16::from_slice(simd, &ENCODE_LUT);
let mut i = 0; let mut b = 0;
while i + 64 <= seq.len() {
pack_lanes::<S, u32x16<S>>(simd, &seq[i..i + 64], lut_block, &mut ebuf[b..b + 16]);
i += 64;
b += 16;
}
if i + 32 <= seq.len() {
pack_lanes::<S, u32x8<S>>(simd, &seq[i..i + 32], lut_block, &mut ebuf[b..b + 8]);
i += 32;
b += 8;
}
if i + 16 <= seq.len() {
pack_lanes::<S, u32x4<S>>(simd, &seq[i..i + 16], lut_block, &mut ebuf[b..b + 4]);
i += 16;
b += 4;
}
if i + 8 <= seq.len() {
pack_8bp_swar(&seq[i..i + 8], &mut ebuf[b..b + 2]);
i += 8;
b += 2;
}
if i < seq.len() {
ebuf[b..seq.len().div_ceil(4)].fill(0);
for (j, &base) in seq[i..].iter().enumerate() {
let code = ((base >> 1) ^ (base >> 2)) & 3; ebuf[b + j / 4] |= code << (2 * (j % 4)); }
}
}
#[inline(always)]
fn pack_lanes<S, V>(simd: S, chunk: &[u8], lut_block: u8x16<S>, out: &mut [u8])
where
S: Simd,
V: SimdNarrow<S>
+ SimdBase<S, Element = u32>
+ Shr<u32, Output = V>
+ BitAnd<Output = V>
+ BitOr<Output = V>,
V::ByteVector: SimdBase<S, Element = u8, Block = u8x16<S>> + BitAnd<Output = V::ByteVector>,
V::Narrowed: SimdNarrow<S>,
<V::Narrowed as SimdNarrow<S>>::Narrowed: SimdBase<S, Element = u8>,
{
let code: V = {
let table = V::ByteVector::block_splat(lut_block); let ascii = V::ByteVector::from_slice(simd, chunk); let idx = ascii & V::ByteVector::simd_from(simd, 6u8); table.swizzle_dyn_within_blocks(idx).bitcast() };
let code = {
let code = code & V::simd_from(simd, 0x03030303u32); let code = code | (code >> 6);
code | (code >> 12)
};
let bytes = {
let halves = code.narrow(code); let quads = halves.narrow(halves); quads
};
out.copy_from_slice(&bytes.as_slice()[..V::N]);
}
#[inline(always)]
fn pack_8bp_swar(chunk: &[u8], ebuf: &mut [u8]) {
let v = u64::from_le_bytes(chunk.try_into().unwrap());
let code = {
let r1 = v >> 1; let r2 = v >> 2; (r1 ^ r2) & 0x0303_0303_0303_0303 };
let packed = {
let code = code | (code >> 6);
code | (code >> 12)
};
ebuf[0] = packed as u8; ebuf[1] = (packed >> 32) as u8; }