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 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(())
}
pub fn pack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
check_vector_len(input.len(), "input")?;
let mut transposed = vec![T::from_u64(0); VALUES];
transpose(input, &mut transposed)?;
pack_transposed(&transposed, width, output)
}
pub fn unpack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
check_vector_len(output.len(), "output")?;
let mut transposed = vec![T::from_u64(0); VALUES];
unpack_transposed(input, width, &mut transposed)?;
untranspose(&transposed, output)
}
#[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)?;
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)?;
if width == 0 {
return Ok(vec![0; count]);
}
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 mut values = Vec::with_capacity(count);
let mut accumulator: u128 = 0;
let mut available = 0usize;
let mut at = 0usize;
for _ in 0..count {
while available < width {
accumulator |= u128::from(input[at]) << available;
at += 1;
available += 8;
}
values.push((accumulator & mask) as u64);
accumulator >>= width;
available -= width;
}
Ok(values)
}
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 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 [0usize, 1, 3, 7, 8, 13, 31, 32, 33, 63, 64] {
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);
}
}
}
#[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);
}
}
}