#[cfg(any(feature = "compression", test))]
use alloc::vec::Vec;
use fearless_simd::{Level, Simd, SimdBase, dispatch, u8x16};
const GROUP: usize = 16;
#[cfg(any(feature = "compression", test))]
pub(crate) fn split(level: Level, src: &[u8], lanes: &mut [Vec<u8>]) {
let width = lanes.len();
let n = src.len() / width;
for lane in lanes.iter_mut() {
lane.clear();
lane.resize(n, 0);
}
let done = match width {
2 => dispatch!(level, simd => split_groups::<_, 2>(simd, src, lanes)),
4 => dispatch!(level, simd => split_groups::<_, 4>(simd, src, lanes)),
8 => dispatch!(level, simd => split_groups::<_, 8>(simd, src, lanes)),
_ => 0,
};
split_scalar(src, lanes, done);
}
#[cfg(any(feature = "compression", test))]
fn split_scalar(src: &[u8], lanes: &mut [Vec<u8>], from: usize) {
let width = lanes.len();
for (index, element) in src.chunks_exact(width).enumerate().skip(from) {
for (lane, &byte) in lanes.iter_mut().zip(element) {
lane[index] = byte;
}
}
}
#[cfg(any(feature = "compression", test))]
#[inline(always)]
fn split_groups<S: Simd, const W: usize>(simd: S, src: &[u8], lanes: &mut [Vec<u8>]) -> usize {
let groups = lanes[0].len() / GROUP;
for (group, bytes) in src.chunks_exact(GROUP * W).take(groups).enumerate() {
let mut v: [u8x16<S>; W] =
core::array::from_fn(|i| u8x16::from_slice(simd, &bytes[i * GROUP..(i + 1) * GROUP]));
for _ in 0..W.trailing_zeros() {
let previous = v;
for j in 0..W / 2 {
v[j] = simd.unzip_low_u8x16(previous[2 * j], previous[2 * j + 1]);
v[W / 2 + j] = simd.unzip_high_u8x16(previous[2 * j], previous[2 * j + 1]);
}
}
for (lane, vector) in lanes.iter_mut().zip(v) {
vector.store_slice(&mut lane[group * GROUP..(group + 1) * GROUP]);
}
}
groups * GROUP
}
#[cfg(any(feature = "decompression", test))]
pub(crate) fn merge(level: Level, dst: &mut [u8], columns: &[&[u8]], n: usize) {
let done = match columns.len() {
2 => dispatch!(level, simd => merge_groups::<_, 2>(simd, dst, columns, n)),
4 => dispatch!(level, simd => merge_groups::<_, 4>(simd, dst, columns, n)),
8 => dispatch!(level, simd => merge_groups::<_, 8>(simd, dst, columns, n)),
_ => 0,
};
merge_scalar(dst, columns, n, done);
}
#[cfg(any(feature = "decompression", test))]
fn merge_scalar(dst: &mut [u8], columns: &[&[u8]], n: usize, from: usize) {
let width = columns.len();
for (position, column) in columns.iter().enumerate() {
for (element, &byte) in dst.chunks_exact_mut(width).zip(&column[..n]).skip(from) {
element[position] = byte;
}
}
}
#[cfg(any(feature = "decompression", test))]
#[inline(always)]
fn merge_groups<S: Simd, const W: usize>(
simd: S,
dst: &mut [u8],
columns: &[&[u8]],
n: usize,
) -> usize {
let groups = n / GROUP;
for (group, out) in dst.chunks_exact_mut(GROUP * W).take(groups).enumerate() {
let mut v: [u8x16<S>; W] = core::array::from_fn(|i| {
u8x16::from_slice(simd, &columns[i][group * GROUP..(group + 1) * GROUP])
});
for _ in 0..W.trailing_zeros() {
let previous = v;
for j in 0..W / 2 {
let (low, high) = simd.interleave_u8x16(previous[j], previous[W / 2 + j]);
v[2 * j] = low;
v[2 * j + 1] = high;
}
}
for (i, vector) in v.into_iter().enumerate() {
vector.store_slice(&mut out[i * GROUP..(i + 1) * GROUP]);
}
}
groups * GROUP
}
#[cfg(all(test, feature = "compression", feature = "decompression"))]
mod tests {
use super::*;
use crate::Backend;
fn sample(len: usize) -> Vec<u8> {
(0..len)
.map(|i| (i.wrapping_mul(0x9E37_79B1) >> 13) as u8)
.collect()
}
#[test]
fn split_matches_the_scalar_columns_at_every_level() {
let counts: &[usize] = if cfg!(miri) {
&[0, 1, 15, 16, 17, 40]
} else {
&[0, 1, 15, 16, 17, 31, 32, 33, 100, 1000]
};
for width in [1usize, 2, 3, 4, 5, 8, 12] {
for &n in counts {
let src = sample(n * width);
let mut expected: Vec<Vec<u8>> = (0..width).map(|_| alloc::vec![0; n]).collect();
split_scalar(&src, &mut expected, 0);
for backend in Backend::available() {
let mut lanes: Vec<Vec<u8>> = (0..width).map(|_| Vec::new()).collect();
split(backend.0, &src, &mut lanes);
assert_eq!(lanes, expected, "{backend} w{width} n{n}");
}
}
}
}
#[test]
fn merge_inverts_split_at_every_level() {
let counts: &[usize] = if cfg!(miri) {
&[0, 1, 16, 17, 40]
} else {
&[0, 1, 15, 16, 17, 33, 100, 1000]
};
for width in [1usize, 2, 3, 4, 8, 16] {
for &n in counts {
let src = sample(n * width);
for backend in Backend::available() {
let mut lanes: Vec<Vec<u8>> = (0..width).map(|_| Vec::new()).collect();
split(backend.0, &src, &mut lanes);
let columns: Vec<&[u8]> = lanes.iter().map(Vec::as_slice).collect();
let mut dst = alloc::vec![0u8; n * width];
merge(backend.0, &mut dst, &columns, n);
assert_eq!(dst, src, "{backend} w{width} n{n}");
}
}
}
}
}