#[cfg(any(feature = "bitpack", feature = "bitunpack", test))]
use alloc::vec;
use alloc::vec::Vec;
#[cfg(any(feature = "bitpack", test))]
use fearless_simd::{Level, Simd, SimdBase, dispatch, u8x64};
#[cfg(any(feature = "bitpack", test))]
pub(crate) const fn encode_bound(nb_elts: usize, nb_bits: u32) -> usize {
(nb_elts * nb_bits as usize).div_ceil(8)
}
#[cfg(any(feature = "bitpack", test))]
pub(crate) fn nb_bits(level: Level, bytes: &[u8], width: usize) -> u32 {
debug_assert!(matches!(width, 1 | 2 | 4 | 8));
let lanes = dispatch!(level, simd => or_lanes(simd, bytes));
nb_bits_of_lanes(&lanes, width)
}
#[cfg(any(feature = "bitpack", test))]
fn nb_bits_of_lanes(lanes: &[u8; LANES], width: usize) -> u32 {
let mut folded = [0u8; 8];
for (position, &lane) in lanes.iter().enumerate() {
folded[position % width] |= lane;
}
1 + u64::from_le_bytes(folded).checked_ilog2().unwrap_or(0)
}
#[cfg(any(feature = "bitpack", test))]
const LANES: usize = 64;
#[cfg(any(feature = "bitpack", test))]
#[inline(always)]
fn or_lanes<S: Simd>(simd: S, bytes: &[u8]) -> [u8; LANES] {
let (blocks, tail) = bytes.as_chunks::<LANES>();
let mut acc = [u8x64::splat(simd, 0), u8x64::splat(simd, 0)];
let (pairs, odd) = blocks.as_chunks::<2>();
for pair in pairs {
acc[0] |= u8x64::from_slice(simd, &pair[0]);
acc[1] |= u8x64::from_slice(simd, &pair[1]);
}
if let Some(block) = odd.first() {
acc[0] |= u8x64::from_slice(simd, block);
}
let mut lanes = [0u8; LANES];
(acc[0] | acc[1]).store_slice(&mut lanes);
for (lane, &byte) in lanes.iter_mut().zip(tail) {
*lane |= byte;
}
lanes
}
#[cfg(test)]
pub(crate) fn nb_bits_scalar(bytes: &[u8], width: usize) -> u32 {
let mut max = 0u64;
for element in bytes.chunks_exact(width) {
let mut buffer = [0u8; 8];
buffer[..width].copy_from_slice(element);
max = max.max(u64::from_le_bytes(buffer));
}
1 + max.checked_ilog2().unwrap_or(0)
}
#[cfg(any(feature = "bitpack", test))]
pub(crate) fn encode(src: &[u8], width: usize, nb_bits: u32) -> Vec<u8> {
let nb_elts = src.len() / width;
if nb_elts == 0 || nb_bits == 0 {
return Vec::new();
}
if nb_bits as usize == width * 8 {
return src[..nb_elts * width].to_vec();
}
let src = &src[..nb_elts * width];
match (width, nb_bits) {
(1, 1..=16) => encode_groups::<1>(src, nb_bits),
(2, 1..=16) => encode_groups::<2>(src, nb_bits),
(4, 1..=16) => encode_groups::<4>(src, nb_bits),
(8, 1..=16) => encode_groups::<8>(src, nb_bits),
_ => encode_scalar(src, width, nb_bits),
}
}
#[cfg(any(feature = "bitpack", test))]
#[inline(always)]
fn element<const W: usize>(group: &[u8], index: usize) -> u64 {
let mut buffer = [0u8; 8];
buffer[..W].copy_from_slice(&group[index * W..index * W + W]);
u64::from_le_bytes(buffer)
}
#[cfg(any(feature = "bitpack", test))]
#[inline(always)]
fn compose<const W: usize>(group: &[u8], nb_bits: u32) -> u128 {
let half = |first: usize| {
element::<W>(group, first)
| element::<W>(group, first + 1) << nb_bits
| element::<W>(group, first + 2) << (2 * nb_bits)
| element::<W>(group, first + 3) << (3 * nb_bits)
};
u128::from(half(0)) | u128::from(half(4)) << (4 * nb_bits)
}
#[cfg(any(feature = "bitpack", test))]
fn encode_groups<const W: usize>(src: &[u8], nb_bits: u32) -> Vec<u8> {
let nb_elts = src.len() / W;
let size = encode_bound(nb_elts, nb_bits);
let step = nb_bits as usize;
let mut out = vec![0u8; size + 16];
let mut at = 0usize;
let full = nb_elts / 8 * 8 * W;
for group in src[..full].chunks_exact(8 * W) {
let value = compose::<W>(group, nb_bits);
out[at..at + 16].copy_from_slice(&value.to_le_bytes());
at += step;
}
if full < src.len() {
let mut last = [0u8; 64];
let rest = &src[full..];
last[..rest.len()].copy_from_slice(rest);
let value = compose::<W>(&last[..8 * W], nb_bits);
out[at..at + 16].copy_from_slice(&value.to_le_bytes());
}
out.truncate(size);
out
}
#[cfg(any(feature = "bitpack", test))]
pub(crate) fn encode_scalar(src: &[u8], width: usize, nb_bits: u32) -> Vec<u8> {
let nb_elts = src.len() / width;
if nb_elts == 0 || nb_bits == 0 {
return Vec::new();
}
if nb_bits as usize == width * 8 {
return src[..nb_elts * width].to_vec();
}
let mut out = Vec::with_capacity(encode_bound(nb_elts, nb_bits));
let mut state = 0u64;
let mut bits = 0u32;
let mut put = |value: u64, count: u32| {
state |= value << bits;
bits += count;
while bits >= 8 {
out.push(state as u8);
state >>= 8;
bits -= 8;
}
};
for element in src.chunks_exact(width) {
let mut buffer = [0u8; 8];
buffer[..width].copy_from_slice(element);
let value = u64::from_le_bytes(buffer);
if nb_bits > 56 {
put(value & 0xFFFF_FFFF, 32);
put(value >> 32, nb_bits - 32);
} else {
put(value, nb_bits);
}
}
if bits > 0 {
out.push(state as u8);
}
out
}
#[cfg(any(feature = "bitunpack", test))]
pub(crate) fn decode(src: &[u8], nb_elts: usize, width: usize, nb_bits: u32) -> (Vec<u8>, usize) {
let used = (nb_elts * nb_bits as usize).div_ceil(8);
let mut out = vec![0u8; nb_elts * width];
if nb_elts == 0 {
return (out, 0);
}
let mask = if nb_bits == 64 {
u64::MAX
} else {
(1u64 << nb_bits) - 1
};
let mut position = 0usize;
for element in out.chunks_exact_mut(width) {
let byte = position / 8;
let shift = (position % 8) as u32;
let mut window = [0u8; 16];
let end = (byte + 9).min(used).min(src.len());
window[..end - byte].copy_from_slice(&src[byte..end]);
let value = (u128::from_le_bytes(window) >> shift) as u64 & mask;
element.copy_from_slice(&value.to_le_bytes()[..width]);
position += nb_bits as usize;
}
(out, used)
}
pub(crate) mod writer;
#[cfg(test)]
mod tests;