use super::{ErrorKind, Kernels};
pub fn unrle(rle: &[u8], out: &mut Vec<u8>, k: Kernels) -> Result<(), ErrorKind> {
#[cfg(target_arch = "x86_64")]
{
if k.avx2 {
return unsafe { unrle_avx2(rle, out) };
}
}
let _ = k;
unrle_scalar(rle, out, 0)
}
pub fn unrle_scalar(rle: &[u8], out: &mut Vec<u8>, mut i: usize) -> Result<(), ErrorKind> {
let n = rle.len();
out.reserve(n - i.min(n));
while i < n {
let c = rle[i];
if i + 3 < n && rle[i + 1] == c && rle[i + 2] == c && rle[i + 3] == c {
let Some(&extra) = rle.get(i + 4) else {
return Err(ErrorKind::RunUnfinished);
};
out.extend_from_slice(&[c, c, c, c]);
out.resize(out.len() + extra as usize, c);
i += 5;
} else {
out.push(c);
i += 1;
}
}
Ok(())
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2,bmi1")]
unsafe fn unrle_avx2(rle: &[u8], out: &mut Vec<u8>) -> Result<(), ErrorKind> {
use std::arch::x86_64::*;
let n = rle.len();
out.reserve(n + 64);
let p = rle.as_ptr();
let mut i = 0;
while i + 35 <= n {
let v0 = _mm256_loadu_si256(p.add(i) as *const __m256i);
let v1 = _mm256_loadu_si256(p.add(i + 1) as *const __m256i);
let v2 = _mm256_loadu_si256(p.add(i + 2) as *const __m256i);
let v3 = _mm256_loadu_si256(p.add(i + 3) as *const __m256i);
let eq = _mm256_and_si256(
_mm256_cmpeq_epi8(v0, v1),
_mm256_and_si256(_mm256_cmpeq_epi8(v0, v2), _mm256_cmpeq_epi8(v0, v3)),
);
let m = _mm256_movemask_epi8(eq) as u32;
if m == 0 {
out.extend_from_slice(&rle[i..i + 32]);
i += 32;
continue;
}
let j = i + m.trailing_zeros() as usize; out.extend_from_slice(&rle[i..j]);
let c = rle[j];
let Some(&extra) = rle.get(j + 4) else {
return Err(ErrorKind::RunUnfinished);
};
out.extend_from_slice(&[c, c, c, c]);
out.resize(out.len() + extra as usize, c);
i = j + 5;
}
unrle_scalar(rle, out, i)
}