use core::ops::{BitAnd, BitOr, Shl};
use fearless_simd::{Level, Simd, dispatch, prelude::*, u8x16, u8x32, u8x64};
use crate::{BitnucError, resize};
pub fn decode(ebuf: &[u8], n: usize, seq: &mut [u8]) -> Result<(), BitnucError> {
if seq.len() < n {
return Err(BitnucError::DecodingBufferTooSmall {
expected: n,
actual: seq.len(),
});
}
if ebuf.len() < n.div_ceil(4) {
return Err(BitnucError::EncodingBufferTooSmall {
expected: n.div_ceil(4),
actual: ebuf.len(),
});
}
let level = Level::new();
dispatch!(level, simd => decode_inner(simd, ebuf, n, seq));
Ok(())
}
#[allow(clippy::uninit_vec)]
pub fn decode_resize(ebuf: &[u8], n: usize, seq: &mut Vec<u8>) -> Result<(), BitnucError> {
if ebuf.len() < n.div_ceil(4) {
return Err(BitnucError::EncodingBufferTooSmall {
expected: n.div_ceil(4),
actual: ebuf.len(),
});
}
resize(seq, n);
let level = Level::new();
dispatch!(level, simd => decode_inner(simd, ebuf, n, &mut seq[..n]));
Ok(())
}
const DECODE_LUT: [u8; 16] = *b"ACGT\0\0\0\0\0\0\0\0\0\0\0\0";
const DECODE_WORDS: [u32; 256] = {
let mut table = [0u32; 256];
let mut p = 0;
while p < 256 {
let mut word = 0u32;
let mut k = 0;
while k < 4 {
word |= (DECODE_LUT[(p >> (2 * k)) & 3] as u32) << (8 * k);
k += 1;
}
table[p] = word;
p += 1;
}
table
};
#[inline(always)]
fn decode_inner<S: Simd>(simd: S, ebuf: &[u8], n: usize, seq: &mut [u8]) {
let lut_block = u8x16::from_slice(simd, &DECODE_LUT);
let mut i = 0; let mut b = 0;
while i + 256 <= n {
unpack_lanes::<S, u8x64<S>, _>(simd, &ebuf[b..b + 64], lut_block, &mut seq[i..i + 256]);
i += 256;
b += 64;
}
if i + 128 <= n {
unpack_lanes::<S, u8x32<S>, _>(simd, &ebuf[b..b + 32], lut_block, &mut seq[i..i + 128]);
i += 128;
b += 32;
}
if i + 64 <= n {
unpack_lanes::<S, u8x16<S>, _>(simd, &ebuf[b..b + 16], lut_block, &mut seq[i..i + 64]);
i += 64;
b += 16;
}
while i + 4 <= n {
let word = DECODE_WORDS[ebuf[b] as usize];
seq[i..i + 4].copy_from_slice(&word.to_le_bytes());
i += 4;
b += 1;
}
for j in i..n {
let code = (ebuf[j / 4] >> (2 * (j % 4))) & 0b11;
seq[j] = DECODE_LUT[code as usize];
}
}
#[inline(always)]
fn unpack_lanes<S, P, Q>(simd: S, chunk: &[u8], lut_block: u8x16<S>, out: &mut [u8])
where
S: Simd,
P: SimdWiden<S> + SimdBase<S, Element = u8, Block = u8x16<S>>,
P::Widened: SimdWiden<S, Widened = Q>,
Q: SimdBase<S, Element = u32, ByteVector = P>
+ Shl<u32, Output = Q>
+ BitOr<Output = Q>
+ BitAnd<Output = Q>,
{
let table = P::block_splat(lut_block);
let packed = P::from_slice(simd, chunk);
let (lo, hi) = packed.widen();
let (q0, q1) = lo.widen();
let (q2, q3) = hi.widen();
for (idx, q) in [q0, q1, q2, q3].into_iter().enumerate() {
let start = idx * P::N;
let end = start + P::N;
spread_lanes(simd, q, table).store_slice(&mut out[start..end]);
}
}
#[inline(always)]
fn spread_lanes<S, V>(simd: S, q: V, table: V::ByteVector) -> V::ByteVector
where
S: Simd,
V: SimdBase<S, Element = u32> + Shl<u32, Output = V> + BitOr<Output = V> + BitAnd<Output = V>,
{
let x = q | (q << 12);
let x = x | (x << 6);
let codes = x & V::simd_from(simd, 0x03030303u32);
table.swizzle_dyn_within_blocks(codes.bitcast::<V::ByteVector>())
}