macro_rules! unpack_impl {
($t:ty, $bytes:literal, $bits:tt) => {
pub fn unpack<const NUM_BITS: usize>(input: &[u8], output: &mut [$t; $bits]) {
if NUM_BITS == 0 {
for out in output {
*out = 0;
}
return;
}
assert!(NUM_BITS <= $bytes * 8);
let mask = match NUM_BITS {
$bits => <$t>::MAX,
_ => ((1 << NUM_BITS) - 1),
};
assert!(input.len() >= NUM_BITS * $bytes);
let r = |output_idx: usize| {
<$t>::from_le_bytes(
input[output_idx * $bytes..output_idx * $bytes + $bytes]
.try_into()
.unwrap(),
)
};
seq_macro::seq!(i in 0..$bits {
let start_bit = i * NUM_BITS;
let end_bit = start_bit + NUM_BITS;
let start_bit_offset = start_bit % $bits;
let end_bit_offset = end_bit % $bits;
let start_byte = start_bit / $bits;
let end_byte = end_bit / $bits;
if start_byte != end_byte && end_bit_offset != 0 {
let val = r(start_byte);
let a = val >> start_bit_offset;
let val = r(end_byte);
let b = val << (NUM_BITS - end_bit_offset);
output[i] = a | (b & mask);
} else {
let val = r(start_byte);
output[i] = (val >> start_bit_offset) & mask;
}
});
}
};
}
macro_rules! unpack {
($name:ident, $t:ty, $bytes:literal, $bits:tt) => {
mod $name {
unpack_impl!($t, $bytes, $bits);
}
pub fn $name(input: &[u8], output: &mut [$t; $bits], num_bits: usize) {
seq_macro::seq!(i in 0..=$bits {
if i == num_bits {
return $name::unpack::<i>(input, output);
}
});
unreachable!("invalid num_bits {}", num_bits);
}
};
}
unpack!(unpack8, u8, 1, 8);
unpack!(unpack16, u16, 2, 16);
unpack!(unpack32, u32, 4, 32);
unpack!(unpack64, u64, 8, 64);
macro_rules! pack_impl {
($t:ty, $bytes:literal, $bits:tt) => {
#[inline(never)]
pub fn pack<const NUM_BITS: usize>(input: &[$t; $bits], output: &mut [u8]) {
if NUM_BITS == 0 {
return;
}
assert!(NUM_BITS <= $bytes * 8);
assert!(output.len() >= NUM_BITS * $bytes);
let mask = match NUM_BITS {
$bits => <$t>::MAX,
_ => ((1 << NUM_BITS) - 1),
};
let mut words = [0; $bits];
seq_macro::seq!(i in 0..$bits {
let value = input[i] & mask;
let start_bit = i * NUM_BITS;
let end_bit = start_bit + NUM_BITS;
let start_bit_offset = start_bit % $bits;
let end_bit_offset = end_bit % $bits;
let start_word = start_bit / $bits;
let end_word = end_bit / $bits;
words[start_word] |= value << start_bit_offset;
if start_word != end_word && end_bit_offset != 0 {
words[end_word] |= value >> (NUM_BITS - end_bit_offset);
}
});
seq_macro::seq!(w in 0..$bits {
if w < NUM_BITS {
output[w * $bytes..(w + 1) * $bytes].copy_from_slice(&words[w].to_le_bytes());
}
});
}
pub fn pack_blocks<const NUM_BITS: usize>(input: &[$t], output: &mut [u8]) {
if NUM_BITS == 0 {
return;
}
let block_bytes = NUM_BITS * $bytes;
let blocks = input.len() / $bits;
assert!(output.len() >= blocks * block_bytes);
for (input, output) in input
.chunks_exact($bits)
.zip(output.chunks_exact_mut(block_bytes))
{
pack::<NUM_BITS>(input.try_into().unwrap(), output);
}
}
};
}
macro_rules! pack {
($name:ident, $blocks:ident, $t:ty, $bytes:literal, $bits:tt) => {
mod $name {
pack_impl!($t, $bytes, $bits);
}
pub fn $name(input: &[$t; $bits], output: &mut [u8], num_bits: usize) {
seq_macro::seq!(i in 0..=$bits {
if i == num_bits {
return $name::pack::<i>(input, output);
}
});
unreachable!("invalid num_bits {}", num_bits);
}
#[inline(never)]
pub(crate) fn $blocks(input: &[$t], output: &mut [u8], num_bits: usize) {
seq_macro::seq!(i in 0..=$bits {
if i == num_bits {
return $name::pack_blocks::<i>(input, output);
}
});
unreachable!("invalid num_bits {}", num_bits);
}
};
}
pack!(pack8, pack8_blocks, u8, 1, 8);
pack!(pack16, pack16_blocks, u16, 2, 16);
pack!(pack32, pack32_blocks, u32, 4, 32);
pack!(pack64, pack64_blocks, u64, 8, 64);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
let input = [0xFF; 4096];
for i in 0..=8 {
let mut output = [0; 8];
unpack8(&input, &mut output, i);
for (idx, out) in output.iter().enumerate() {
assert_eq!(out.trailing_ones() as usize, i, "out[{idx}] = {out}");
}
}
for i in 0..=16 {
let mut output = [0; 16];
unpack16(&input, &mut output, i);
for (idx, out) in output.iter().enumerate() {
assert_eq!(out.trailing_ones() as usize, i, "out[{idx}] = {out}");
}
}
for i in 0..=32 {
let mut output = [0; 32];
unpack32(&input, &mut output, i);
for (idx, out) in output.iter().enumerate() {
assert_eq!(out.trailing_ones() as usize, i, "out[{idx}] = {out}");
}
}
for i in 0..=64 {
let mut output = [0; 64];
unpack64(&input, &mut output, i);
for (idx, out) in output.iter().enumerate() {
assert_eq!(out.trailing_ones() as usize, i, "out[{idx}] = {out}");
}
}
}
#[test]
fn test_pack_all_ones() {
let mut output = [0u8; 4096];
for i in 0..=8 {
output.fill(0);
pack8(&[u8::MAX; 8], &mut output, i);
assert!(output[..i].iter().all(|&b| b == u8::MAX), "num_bits = {i}");
assert!(output[i..].iter().all(|&b| b == 0), "num_bits = {i}");
}
for i in 0..=16 {
output.fill(0);
pack16(&[u16::MAX; 16], &mut output, i);
assert!(
output[..2 * i].iter().all(|&b| b == u8::MAX),
"num_bits = {i}"
);
assert!(output[2 * i..].iter().all(|&b| b == 0), "num_bits = {i}");
}
for i in 0..=32 {
output.fill(0);
pack32(&[u32::MAX; 32], &mut output, i);
assert!(
output[..4 * i].iter().all(|&b| b == u8::MAX),
"num_bits = {i}"
);
assert!(output[4 * i..].iter().all(|&b| b == 0), "num_bits = {i}");
}
for i in 0..=64 {
output.fill(0);
pack64(&[u64::MAX; 64], &mut output, i);
assert!(
output[..8 * i].iter().all(|&b| b == u8::MAX),
"num_bits = {i}"
);
assert!(output[8 * i..].iter().all(|&b| b == 0), "num_bits = {i}");
}
}
#[test]
fn test_pack_round_trip() {
use crate::util::test_common::rand_gen::random_numbers;
for i in 0..=8 {
let input: [u8; 8] = random_numbers(8).try_into().unwrap();
let mut packed = vec![0u8; i];
pack8(&input, &mut packed, i);
let mut output = [0; 8];
unpack8(&packed, &mut output, i);
let mask = ((1u16 << i) - 1) as u8;
for (idx, (&v, &out)) in input.iter().zip(output.iter()).enumerate() {
assert_eq!(v & mask, out, "num_bits = {i}, index = {idx}");
}
}
for i in 0..=16 {
let input: [u16; 16] = random_numbers(16).try_into().unwrap();
let mut packed = vec![0u8; 2 * i];
pack16(&input, &mut packed, i);
let mut output = [0; 16];
unpack16(&packed, &mut output, i);
let mask = ((1u32 << i) - 1) as u16;
for (idx, (&v, &out)) in input.iter().zip(output.iter()).enumerate() {
assert_eq!(v & mask, out, "num_bits = {i}, index = {idx}");
}
}
for i in 0..=32 {
let input: [u32; 32] = random_numbers(32).try_into().unwrap();
let mut packed = vec![0u8; 4 * i];
pack32(&input, &mut packed, i);
let mut output = [0; 32];
unpack32(&packed, &mut output, i);
let mask = ((1u64 << i) - 1) as u32;
for (idx, (&v, &out)) in input.iter().zip(output.iter()).enumerate() {
assert_eq!(v & mask, out, "num_bits = {i}, index = {idx}");
}
}
for i in 0..=64 {
let input: [u64; 64] = random_numbers(64).try_into().unwrap();
let mut packed = vec![0u8; 8 * i];
pack64(&input, &mut packed, i);
let mut output = [0; 64];
unpack64(&packed, &mut output, i);
let mask = ((1u128 << i) - 1) as u64;
for (idx, (&v, &out)) in input.iter().zip(output.iter()).enumerate() {
assert_eq!(v & mask, out, "num_bits = {i}, index = {idx}");
}
}
}
}