use rudb_common::{Error, Result};
pub const VALUES: usize = 1024;
const ORDER: [usize; 8] = [0, 4, 2, 6, 1, 5, 3, 7];
mod sealed {
pub trait Sealed {}
impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
}
pub trait Packable: sealed::Sealed + Copy + Ord + std::fmt::Debug {
const WIDTH: usize;
const LANES: usize = VALUES / Self::WIDTH;
fn to_u64(self) -> u64;
fn from_u64(value: u64) -> Self;
}
macro_rules! impl_packable {
($($ty:ty),*) => {$(
impl Packable for $ty {
const WIDTH: usize = <$ty>::BITS as usize;
#[inline]
fn to_u64(self) -> u64 {
u64::from(self)
}
#[inline]
fn from_u64(value: u64) -> Self {
value as $ty
}
}
)*};
}
impl_packable!(u8, u16, u32, u64);
#[inline]
const fn low_mask(bits: usize) -> u64 {
if bits >= 64 { u64::MAX } else { (1u64 << bits) - 1 }
}
#[inline]
const fn shift_right(value: u64, bits: usize) -> u64 {
if bits >= 64 { 0 } else { value >> bits }
}
#[inline]
#[must_use]
pub fn source_index<T: Packable>(row: usize, lane: usize) -> usize {
assert!(row < T::WIDTH, "row {row} is outside a {} bit type", T::WIDTH);
assert!(lane < T::LANES, "lane {lane} is outside {} lanes", T::LANES);
let group_size = T::WIDTH / 8;
let group = row / group_size;
let offset = row % group_size;
((offset * 8) + ORDER[group]) * T::LANES + lane
}
pub fn transpose<T: Packable>(input: &[T], output: &mut [T]) -> Result<()> {
check_vector_len(input.len(), "input")?;
check_vector_len(output.len(), "output")?;
for row in 0..T::WIDTH {
for lane in 0..T::LANES {
output[row * T::LANES + lane] = input[source_index::<T>(row, lane)];
}
}
Ok(())
}
pub fn untranspose<T: Packable>(input: &[T], output: &mut [T]) -> Result<()> {
check_vector_len(input.len(), "input")?;
check_vector_len(output.len(), "output")?;
for row in 0..T::WIDTH {
for lane in 0..T::LANES {
output[source_index::<T>(row, lane)] = input[row * T::LANES + lane];
}
}
Ok(())
}
#[must_use]
pub fn packed_len<T: Packable>(width: usize) -> usize {
width * T::LANES
}
#[must_use]
pub fn unit_len(width: usize) -> usize {
packed_len::<u64>(width) * size_of::<u64>()
}
#[must_use]
pub fn required_width<T: Packable>(values: &[T]) -> usize {
let max = values.iter().copied().max().map_or(0, T::to_u64);
(64 - max.leading_zeros()) as usize
}
pub fn pack_transposed<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
check_vector_len(input.len(), "input")?;
check_width::<T>(width)?;
if output.len() != packed_len::<T>(width) {
return Err(Error::internal(format!(
"a {width} bit packed vector is {} words, not {}",
packed_len::<T>(width),
output.len()
)));
}
if width == 0 {
return check_all_zero(input);
}
let mask = low_mask(width);
let lanes = T::LANES;
for lane in 0..lanes {
let mut filled = 0usize;
let mut accumulator = 0u64;
let mut word = 0usize;
for row in 0..T::WIDTH {
let value = input[row * lanes + lane].to_u64();
if value & !mask != 0 {
return Err(Error::internal(format!("value {value} does not fit in {width} bits")));
}
accumulator |= value << filled;
filled += width;
if filled >= T::WIDTH {
output[word * lanes + lane] = T::from_u64(accumulator & low_mask(T::WIDTH));
word += 1;
let consumed = width - (filled - T::WIDTH);
filled -= T::WIDTH;
accumulator = shift_right(value, consumed);
}
}
debug_assert_eq!(filled, 0, "a packed lane always ends on a word boundary");
}
Ok(())
}
pub fn unpack_transposed<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
check_width::<T>(width)?;
check_vector_len(output.len(), "output")?;
if input.len() != packed_len::<T>(width) {
return Err(Error::internal(format!(
"a {width} bit packed vector is {} words, not {}",
packed_len::<T>(width),
input.len()
)));
}
if width == 0 {
output.fill(T::from_u64(0));
return Ok(());
}
let mask = low_mask(width);
let lanes = T::LANES;
for lane in 0..lanes {
let mut available = 0usize;
let mut buffer = 0u64;
let mut word = 0usize;
for row in 0..T::WIDTH {
let value = if available >= width {
let value = buffer & mask;
buffer = shift_right(buffer, width);
available -= width;
value
} else {
let next = input[word * lanes + lane].to_u64();
word += 1;
let taken = width - available;
let value = buffer | ((next & low_mask(taken)) << available);
buffer = shift_right(next, taken);
available = T::WIDTH - taken;
value
};
output[row * lanes + lane] = T::from_u64(value);
}
}
Ok(())
}
#[derive(Debug)]
pub struct Scratch<T: Packable> {
transposed: Vec<T>,
}
impl<T: Packable> Scratch<T> {
#[must_use]
pub const fn new() -> Self {
Self { transposed: Vec::new() }
}
fn ready(&mut self) {
if self.transposed.len() != VALUES {
self.transposed.resize(VALUES, T::from_u64(0));
}
}
}
impl<T: Packable> Default for Scratch<T> {
fn default() -> Self {
Self::new()
}
}
pub fn pack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
pack_with(input, width, output, &mut Scratch::new())
}
pub fn pack_with<T: Packable>(
input: &[T],
width: usize,
output: &mut [T],
scratch: &mut Scratch<T>,
) -> Result<()> {
check_vector_len(input.len(), "input")?;
scratch.ready();
transpose(input, &mut scratch.transposed)?;
pack_transposed(&scratch.transposed, width, output)
}
pub fn unpack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
unpack_mapped(input, width, output, T::from_u64)
}
pub fn unpack_mapped<T: Packable, U: Copy>(
input: &[T],
width: usize,
output: &mut [U],
value: impl Fn(u64) -> U,
) -> Result<()> {
check_width::<T>(width)?;
check_vector_len(output.len(), "output")?;
if input.len() != packed_len::<T>(width) {
return Err(Error::internal(format!(
"a {width} bit packed vector is {} words, not {}",
packed_len::<T>(width),
input.len()
)));
}
if width == 0 {
output.fill(value(0));
return Ok(());
}
let mask = low_mask(width);
let lanes = T::LANES;
let group_size = T::WIDTH / 8;
for row in 0..T::WIDTH {
let bit = row * width;
let word = bit / T::WIDTH;
let shift = bit % T::WIDTH;
let base = ((row % group_size) * 8 + ORDER[row / group_size]) * lanes;
let low = &input[word * lanes..(word + 1) * lanes];
let into = &mut output[base..base + lanes];
if shift + width <= T::WIDTH {
for lane in 0..lanes {
into[lane] = value((low[lane].to_u64() >> shift) & mask);
}
} else {
let carried = T::WIDTH - shift;
let high = &input[(word + 1) * lanes..(word + 2) * lanes];
for lane in 0..lanes {
let bits = (low[lane].to_u64() >> shift) | (high[lane].to_u64() << carried);
into[lane] = value(bits & mask);
}
}
}
Ok(())
}
pub fn unpack_unit_into<U: Copy>(
input: &[u8],
width: usize,
output: &mut [U],
value: impl Fn(u64) -> U,
) -> Result<()> {
check_width::<u64>(width)?;
check_vector_len(output.len(), "output")?;
if input.len() != unit_len(width) {
return Err(Error::internal(format!(
"a {width} bit packed vector is {} bytes, not {}",
unit_len(width),
input.len()
)));
}
if width == 0 {
output.fill(value(0));
return Ok(());
}
let mask = low_mask(width);
let lanes = <u64 as Packable>::LANES;
let stride = lanes * size_of::<u64>();
for row in 0..u64::BITS as usize {
let bit = row * width;
let word = bit / u64::BITS as usize;
let shift = bit % u64::BITS as usize;
let base = ((row % 8) * 8 + ORDER[row / 8]) * lanes;
let low = &input[word * stride..(word + 1) * stride];
let into = &mut output[base..base + lanes];
if shift + width <= u64::BITS as usize {
for (lane, slot) in into.iter_mut().enumerate() {
*slot = value((word_at(low, lane * size_of::<u64>()) >> shift) & mask);
}
} else {
let carried = u64::BITS as usize - shift;
let high = &input[(word + 1) * stride..(word + 2) * stride];
for (lane, slot) in into.iter_mut().enumerate() {
let at = lane * size_of::<u64>();
let bits = (word_at(low, at) >> shift) | (word_at(high, at) << carried);
*slot = value(bits & mask);
}
}
}
Ok(())
}
pub fn unpack_u64_at(input: &[u8], width: usize, index: usize) -> Result<u64> {
check_width::<u64>(width)?;
if index >= VALUES {
return Err(Error::internal(format!(
"packed value {index} is outside a {VALUES} value unit"
)));
}
let expected = packed_len::<u64>(width) * size_of::<u64>();
if input.len() != expected {
return Err(Error::internal(format!(
"a {width} bit packed vector is {expected} bytes, not {}",
input.len()
)));
}
if width == 0 {
return Ok(0);
}
let lanes = <u64 as Packable>::LANES;
let block = index / lanes;
let lane = index % lanes;
let group = ORDER[block % 8];
let row = group * (<u64 as Packable>::WIDTH / 8) + block / 8;
let bit = row * width;
let word = bit / <u64 as Packable>::WIDTH;
let shift = bit % <u64 as Packable>::WIDTH;
let low = word_at(input, (word * lanes + lane) * size_of::<u64>());
let bits = if shift + width <= <u64 as Packable>::WIDTH {
low >> shift
} else {
let high = word_at(input, ((word + 1) * lanes + lane) * size_of::<u64>());
(low >> shift) | (high << (<u64 as Packable>::WIDTH - shift))
};
Ok(bits & low_mask(width))
}
#[must_use]
pub fn tail_len(count: usize, width: usize) -> usize {
(count * width).div_ceil(8)
}
pub fn pack_tail(values: &[u64], width: usize, output: &mut Vec<u8>) -> Result<()> {
check_tail(values.len(), width)?;
pack_linear(values, width, output)
}
pub fn pack_linear(values: &[u64], width: usize, output: &mut Vec<u8>) -> Result<()> {
if width > 64 {
return Err(Error::internal(format!("{width} bits does not fit in 64")));
}
if width == 0 {
return check_all_zero(values);
}
let mask = low_mask(width);
let mut accumulator: u128 = 0;
let mut filled = 0usize;
for value in values {
if value & !mask != 0 {
return Err(Error::internal(format!("value {value} does not fit in {width} bits")));
}
accumulator |= u128::from(*value) << filled;
filled += width;
while filled >= 8 {
output.push((accumulator & 0xff) as u8);
accumulator >>= 8;
filled -= 8;
}
}
if filled > 0 {
output.push((accumulator & 0xff) as u8);
}
Ok(())
}
pub fn unpack_tail(input: &[u8], width: usize, count: usize) -> Result<Vec<u64>> {
check_tail(count, width)?;
let mut values = vec![0u64; count];
unpack_tail_into(input, width, &mut values, |bits| bits)?;
Ok(values)
}
pub fn unpack_tail_into<U: Copy>(
input: &[u8],
width: usize,
output: &mut [U],
value: impl Fn(u64) -> U,
) -> Result<()> {
let count = output.len();
check_tail(count, width)?;
if width == 0 {
output.fill(value(0));
return Ok(());
}
if input.len() < tail_len(count, width) {
return Err(Error::internal(format!(
"{count} values at {width} bits need {} bytes and there are {}",
tail_len(count, width),
input.len()
)));
}
let mask = u128::from(low_mask(width));
let read = |window: u128, bit: usize| ((window >> bit) & mask) as u64;
if input.len() < WINDOW {
let mut window = [0u8; WINDOW];
window[..input.len()].copy_from_slice(input);
let word = u128::from_le_bytes(window);
for (index, slot) in output.iter_mut().enumerate() {
*slot = value(read(word, index * width));
}
return Ok(());
}
let whole = (((input.len() - WINDOW) * 8) / width + 1).min(count);
if width <= NARROW {
let mask = low_mask(width);
for (index, slot) in output[..whole].iter_mut().enumerate() {
let bit = index * width;
let word = word_at(input, bit / 8);
*slot = value((word >> (bit % 8)) & mask);
}
} else {
for (index, slot) in output[..whole].iter_mut().enumerate() {
let bit = index * width;
let mut window = [0u8; WINDOW];
window.copy_from_slice(&input[bit / 8..bit / 8 + WINDOW]);
*slot = value(read(u128::from_le_bytes(window), bit % 8));
}
}
if whole < count {
let base = input.len() - WINDOW;
let mut window = [0u8; WINDOW];
window.copy_from_slice(&input[base..]);
let word = u128::from_le_bytes(window);
for (offset, slot) in output[whole..].iter_mut().enumerate() {
*slot = value(read(word, (whole + offset) * width - base * 8));
}
}
Ok(())
}
#[inline]
pub fn tail_at(input: &[u8], width: usize, index: usize) -> Result<u64> {
if width > 64 {
return Err(Error::internal(format!("a width of {width} is past what a u64 holds")));
}
if width == 0 {
return Ok(0);
}
let start = index * width;
let end = start + width;
if end.div_ceil(8) > input.len() {
return Err(Error::internal(format!(
"value {index} at {width} bits ends past the {} bytes there are",
input.len()
)));
}
let first = start / 8;
let last = (end - 1) / 8;
if first + 8 <= input.len() && last - first < 8 {
return Ok((word_at(input, first) >> (start % 8)) & low_mask(width));
}
let mut window = [0u8; WINDOW];
window[..=last - first].copy_from_slice(&input[first..=last]);
let word = u128::from_le_bytes(window);
Ok(((word >> (start % 8)) & u128::from(low_mask(width))) as u64)
}
#[inline]
pub fn tail_pair(input: &[u8], width: usize, index: usize) -> Result<(u64, u64)> {
let Some(before) = index.checked_sub(1) else {
return Err(Error::internal("a tail pair has nothing before its first value"));
};
if width == 0 {
return Ok((0, 0));
}
let start = before * width;
let shift = start % 8;
let first = start / 8;
if shift + 2 * width <= u64::BITS as usize && first + 8 <= input.len() {
let word = word_at(input, first) >> shift;
let mask = low_mask(width);
return Ok((word & mask, (word >> width) & mask));
}
Ok((tail_at(input, width, before)?, tail_at(input, width, index)?))
}
const WINDOW: usize = 16;
const NARROW: usize = 57;
#[inline]
fn word_at(input: &[u8], at: usize) -> u64 {
let run: [u8; 8] = input[at..at + 8].try_into().expect("eight bytes");
u64::from_le_bytes(run)
}
fn check_tail(count: usize, width: usize) -> Result<()> {
if count >= VALUES {
return Err(Error::internal(format!(
"{count} values is a whole unit and belongs in the transposed layout"
)));
}
if width > 64 {
return Err(Error::internal(format!("{width} bits does not fit in 64")));
}
Ok(())
}
fn check_vector_len(len: usize, what: &str) -> Result<()> {
if len == VALUES {
Ok(())
} else {
Err(Error::internal(format!("{what} is {len} values, and a packed unit is {VALUES}")))
}
}
fn check_width<T: Packable>(width: usize) -> Result<()> {
if width <= T::WIDTH {
Ok(())
} else {
Err(Error::internal(format!("{width} bits does not fit in a {} bit type", T::WIDTH)))
}
}
fn check_all_zero<T: Packable>(input: &[T]) -> Result<()> {
match input.iter().position(|value| value.to_u64() != 0) {
None => Ok(()),
Some(index) => Err(Error::internal(format!(
"a zero bit vector cannot hold {:?} at {index}",
input[index]
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Random(u64);
impl Random {
fn new() -> Self {
Self(0x2545_f491_4f6c_dd1d)
}
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
}
fn sample<T: Packable>(width: usize) -> Vec<T> {
let mut random = Random::new();
(0..VALUES).map(|_| T::from_u64(random.next() & low_mask(width))).collect()
}
fn round_trip<T: Packable>(width: usize) {
let values = sample::<T>(width);
let mut packed = vec![T::from_u64(0); packed_len::<T>(width)];
pack(&values, width, &mut packed).unwrap();
let mut back = vec![T::from_u64(0); VALUES];
unpack(&packed, width, &mut back).unwrap();
assert_eq!(back, values, "{width} bits of a {} bit type", T::WIDTH);
}
#[test]
fn every_width_of_every_type_round_trips() {
for width in 0..=8 {
round_trip::<u8>(width);
}
for width in 0..=16 {
round_trip::<u16>(width);
}
for width in 0..=32 {
round_trip::<u32>(width);
}
for width in 0..=64 {
round_trip::<u64>(width);
}
}
#[test]
fn one_value_from_a_full_u64_unit_agrees_with_a_whole_unpack() {
for width in 0..=64 {
let values = sample::<u64>(width);
let mut packed = vec![0u64; packed_len::<u64>(width)];
pack(&values, width, &mut packed).unwrap();
let bytes = packed.iter().flat_map(|word| word.to_le_bytes()).collect::<Vec<_>>();
for (index, expected) in values.iter().enumerate() {
assert_eq!(
unpack_u64_at(&bytes, width, index).unwrap(),
*expected,
"value {index} at {width} bits"
);
}
}
}
#[test]
fn a_unit_unpacked_from_bytes_gives_what_one_unpacked_from_words_gives() {
for width in 0..=64 {
let values = sample::<u64>(width);
let mut packed = vec![0u64; packed_len::<u64>(width)];
pack(&values, width, &mut packed).unwrap();
let bytes = packed.iter().flat_map(|word| word.to_le_bytes()).collect::<Vec<_>>();
assert_eq!(bytes.len(), unit_len(width), "at {width} bits");
let map = |offset: u64| offset.wrapping_add(0x1234_5678) as i64;
let mut from_words = vec![0i64; VALUES];
unpack_mapped(&packed, width, &mut from_words, map).unwrap();
let mut from_bytes = vec![0i64; VALUES];
unpack_unit_into(&bytes, width, &mut from_bytes, map).unwrap();
assert_eq!(from_bytes, from_words, "at {width} bits");
let mut moved = vec![0u8; bytes.len() + 3];
moved[3..].copy_from_slice(&bytes);
let mut from_moved = vec![0i64; VALUES];
unpack_unit_into(&moved[3..], width, &mut from_moved, map).unwrap();
assert_eq!(from_moved, from_words, "at {width} bits, three bytes along");
}
}
#[test]
fn a_unit_of_the_wrong_length_is_refused() {
let mut out = vec![0i64; VALUES];
let bytes = vec![0u8; unit_len(9) - 1];
assert!(unpack_unit_into(&bytes, 9, &mut out, |bits| bits as i64).is_err());
let bytes = vec![0u8; unit_len(9) + 1];
assert!(unpack_unit_into(&bytes, 9, &mut out, |bits| bits as i64).is_err());
let bytes = vec![0u8; unit_len(65)];
assert!(unpack_unit_into(&bytes, 65, &mut out, |bits| bits as i64).is_err());
let bytes = vec![0u8; unit_len(9)];
assert!(unpack_unit_into(&bytes, 9, &mut out[..VALUES - 1], |bits| bits as i64).is_err());
}
#[test]
fn a_reused_scratch_gives_what_a_fresh_one_gives() {
let mut scratch = Scratch::<u64>::new();
for width in [64, 1, 33, 7, 64, 0, 17, 60, 3] {
let values = sample::<u64>(width);
let mut reused = vec![0u64; packed_len::<u64>(width)];
pack_with(&values, width, &mut reused, &mut scratch).unwrap();
let mut fresh = vec![0u64; packed_len::<u64>(width)];
pack(&values, width, &mut fresh).unwrap();
assert_eq!(reused, fresh, "at {width} bits after a wider unit");
let mut back = vec![0u64; VALUES];
unpack(&reused, width, &mut back).unwrap();
assert_eq!(back, values, "at {width} bits");
}
}
#[test]
fn the_one_pass_unpack_gives_what_the_two_passes_give() {
fn agree<T: Packable>() {
for width in 0..=T::WIDTH {
let values = sample::<T>(width);
let mut transposed = vec![T::from_u64(0); VALUES];
transpose(&values, &mut transposed).unwrap();
let mut packed = vec![T::from_u64(0); packed_len::<T>(width)];
pack_transposed(&transposed, width, &mut packed).unwrap();
let mut middle = vec![T::from_u64(0); VALUES];
unpack_transposed(&packed, width, &mut middle).unwrap();
let mut slow = vec![T::from_u64(0); VALUES];
untranspose(&middle, &mut slow).unwrap();
let mut fast = vec![T::from_u64(0); VALUES];
unpack(&packed, width, &mut fast).unwrap();
assert_eq!(fast, slow, "{} bit type at {width} bits", T::WIDTH);
assert_eq!(fast, values, "{} bit type at {width} bits round trip", T::WIDTH);
}
}
agree::<u8>();
agree::<u16>();
agree::<u32>();
agree::<u64>();
}
#[test]
fn a_mapped_unpack_gives_what_unpacking_and_then_mapping_gives() {
for width in 0..=64 {
let values = sample::<u64>(width);
let mut packed = vec![0u64; packed_len::<u64>(width)];
pack(&values, width, &mut packed).unwrap();
let base = -7i64;
let mut mapped = vec![0i64; VALUES];
unpack_mapped(&packed, width, &mut mapped, |offset| {
(i128::from(base) + i128::from(offset)) as i64
})
.unwrap();
let mut plain = vec![0u64; VALUES];
unpack(&packed, width, &mut plain).unwrap();
let expected: Vec<i64> = plain
.iter()
.map(|offset| (i128::from(base) + i128::from(*offset)) as i64)
.collect();
assert_eq!(mapped, expected, "at {width} bits");
}
}
#[test]
fn a_mapped_tail_gives_what_unpacking_the_tail_and_then_mapping_gives() {
for width in [0usize, 1, 7, 17, 32, 57, 58, 64] {
for count in [1usize, 2, 63, 64, 300, 1023] {
let values: Vec<u64> = sample::<u64>(width).into_iter().take(count).collect();
let mut packed = Vec::new();
pack_tail(&values, width, &mut packed).unwrap();
let base = 11i64;
let mut mapped = vec![0i64; count];
unpack_tail_into(&packed, width, &mut mapped, |offset| {
(i128::from(base) + i128::from(offset)) as i64
})
.unwrap();
let plain = unpack_tail(&packed, width, count).unwrap();
let expected: Vec<i64> = plain
.iter()
.map(|offset| (i128::from(base) + i128::from(*offset)) as i64)
.collect();
assert_eq!(mapped, expected, "{count} values at {width} bits");
}
}
}
#[test]
fn the_transposed_form_also_round_trips_without_being_reordered() {
let values = sample::<u32>(19);
let mut transposed = vec![0u32; VALUES];
transpose(&values, &mut transposed).unwrap();
let mut packed = vec![0u32; packed_len::<u32>(19)];
pack_transposed(&transposed, 19, &mut packed).unwrap();
let mut back = vec![0u32; VALUES];
unpack_transposed(&packed, 19, &mut back).unwrap();
assert_eq!(back, transposed);
}
#[test]
fn the_permutation_is_a_bijection() {
fn check<T: Packable>() {
let mut seen = vec![false; VALUES];
for row in 0..T::WIDTH {
for lane in 0..T::LANES {
let index = source_index::<T>(row, lane);
assert!(!seen[index], "{index} is written twice for {} bits", T::WIDTH);
seen[index] = true;
}
}
assert!(seen.into_iter().all(|hit| hit));
}
check::<u8>();
check::<u16>();
check::<u32>();
check::<u64>();
}
#[test]
fn transposing_is_not_the_identity() {
let values: Vec<u32> = (0..VALUES).map(|index| index as u32).collect();
let mut transposed = vec![0u32; VALUES];
transpose(&values, &mut transposed).unwrap();
assert_ne!(transposed, values);
let mut back = vec![0u32; VALUES];
untranspose(&transposed, &mut back).unwrap();
assert_eq!(back, values);
}
#[test]
fn a_full_width_pack_is_the_data_itself() {
let values = sample::<u64>(64);
let mut transposed = vec![0u64; VALUES];
transpose(&values, &mut transposed).unwrap();
let mut packed = vec![0u64; packed_len::<u64>(64)];
pack_transposed(&transposed, 64, &mut packed).unwrap();
assert_eq!(packed, transposed);
}
#[test]
fn a_zero_width_vector_stores_nothing_and_reads_back_as_zeros() {
let values = vec![0u32; VALUES];
assert_eq!(required_width(&values), 0);
let mut packed = Vec::new();
pack(&values, 0, &mut packed).unwrap();
let mut back = vec![7u32; VALUES];
unpack(&packed, 0, &mut back).unwrap();
assert_eq!(back, values);
}
#[test]
fn required_width_is_the_bits_of_the_largest_value() {
assert_eq!(required_width::<u32>(&[]), 0);
assert_eq!(required_width::<u32>(&[0, 0]), 0);
assert_eq!(required_width::<u32>(&[1]), 1);
assert_eq!(required_width::<u32>(&[255, 3]), 8);
assert_eq!(required_width::<u32>(&[256]), 9);
assert_eq!(required_width::<u64>(&[u64::MAX]), 64);
}
#[test]
fn a_value_too_wide_for_the_width_is_an_error_rather_than_silent_truncation() {
let mut values = vec![0u32; VALUES];
values[500] = 8;
let mut transposed = vec![0u32; VALUES];
transpose(&values, &mut transposed).unwrap();
let mut packed = vec![0u32; packed_len::<u32>(3)];
let error = pack_transposed(&transposed, 3, &mut packed).unwrap_err();
assert!(error.message().contains("does not fit in 3 bits"), "{error}");
}
#[test]
fn a_wrong_sized_buffer_is_an_error() {
let values = vec![0u32; VALUES];
let mut packed = vec![0u32; 3];
let error = pack(&values, 5, &mut packed).unwrap_err();
assert!(error.message().contains("words"), "{error}");
let short = vec![0u32; 7];
let mut output = vec![0u32; VALUES];
let error = unpack(&short, 5, &mut output).unwrap_err();
assert!(error.message().contains("words"), "{error}");
}
#[test]
fn a_nonzero_value_at_zero_width_is_an_error() {
let mut values = vec![0u32; VALUES];
values[9] = 1;
let mut packed = Vec::new();
let error = pack(&values, 0, &mut packed).unwrap_err();
assert!(error.message().contains("zero bit vector"), "{error}");
}
#[test]
fn packing_at_a_width_the_type_cannot_hold_is_an_error() {
let values = vec![0u16; VALUES];
let mut packed = vec![0u16; 17 * 64];
let error = pack(&values, 17, &mut packed).unwrap_err();
assert!(error.message().contains("16 bit type"), "{error}");
}
#[test]
fn a_tail_round_trips_at_every_width_and_every_length() {
let mut random = Random::new();
for width in 0..=64usize {
for count in [0usize, 1, 2, 7, 8, 9, 100, 1023] {
let values: Vec<u64> =
(0..count).map(|_| random.next() & low_mask(width)).collect();
let mut bytes = Vec::new();
pack_tail(&values, width, &mut bytes).unwrap();
assert_eq!(bytes.len(), tail_len(count, width), "{count} at {width}");
assert_eq!(
unpack_tail(&bytes, width, count).unwrap(),
values,
"{count} at {width}"
);
}
}
}
#[test]
fn one_value_of_a_tail_reads_the_same_as_the_whole_of_it() {
let mut random = Random::new();
for width in 0..=64usize {
let count = 37;
let values: Vec<u64> = (0..count).map(|_| random.next() & low_mask(width)).collect();
let mut bytes = Vec::new();
pack_tail(&values, width, &mut bytes).unwrap();
for (index, value) in values.iter().enumerate() {
assert_eq!(tail_at(&bytes, width, index).unwrap(), *value, "{index} at {width}");
}
let Some(fits) = (bytes.len() * 8).checked_div(width) else { continue };
assert!(tail_at(&bytes, width, fits + 1).is_err(), "past the end at {width}");
}
}
#[test]
fn the_padded_end_of_a_tail_reads_the_same_as_the_windowed_start() {
let mut random = Random::new();
for width in 1..=64usize {
for count in [1usize, 2, 3, 17, 129, 1023] {
let values: Vec<u64> =
(0..count).map(|_| random.next() & low_mask(width)).collect();
let mut exact = Vec::new();
pack_tail(&values, width, &mut exact).unwrap();
let mut slack = exact.clone();
slack.extend_from_slice(&[0u8; WINDOW]);
assert_eq!(
unpack_tail(&exact, width, count).unwrap(),
values,
"{count} at {width}"
);
assert_eq!(
unpack_tail(&slack, width, count).unwrap(),
values,
"{count} at {width}"
);
}
}
}
#[test]
fn a_pair_of_tail_values_reads_the_same_as_the_two_of_them_apart() {
let mut random = Random::new();
for width in 0..=64usize {
let count = 37;
let values: Vec<u64> = (0..count).map(|_| random.next() & low_mask(width)).collect();
let mut bytes = Vec::new();
pack_tail(&values, width, &mut bytes).unwrap();
for index in 1..count {
assert_eq!(
tail_pair(&bytes, width, index).unwrap(),
(values[index - 1], values[index]),
"{index} at {width}"
);
}
assert!(tail_pair(&bytes, width, 0).is_err(), "nothing before the first at {width}");
}
}
#[test]
fn a_tail_costs_its_own_values_and_not_a_whole_unit() {
let values = vec![(1u64 << 39) + 1; 3];
let mut bytes = Vec::new();
pack_tail(&values, 40, &mut bytes).unwrap();
assert_eq!(bytes.len(), 15);
assert_eq!(packed_len::<u64>(40) * 8, 5120);
}
#[test]
fn a_whole_unit_is_refused_by_the_tail_packer() {
let values = vec![0u64; VALUES];
let error = pack_tail(&values, 4, &mut Vec::new()).unwrap_err();
assert!(error.message().contains("whole unit"), "{error}");
}
#[test]
fn a_short_tail_buffer_is_an_error() {
let error = unpack_tail(&[0, 0], 8, 5).unwrap_err();
assert!(error.message().contains("need 5 bytes"), "{error}");
}
#[test]
fn the_packed_size_is_the_same_as_the_naive_layout() {
for width in 0..=32 {
assert_eq!(packed_len::<u32>(width) * 32, width * VALUES);
}
}
}