use super::{Ctx, Desc};
use crate::decompressor::core::data::{Input, Stream, Type, corrupt};
use crate::decompressor::error::DecodeError;
pub(crate) const BITPACK_SERIAL: Desc = Desc {
id: 27,
min_version: 3,
regens: &[Type::Serial],
variable_regens: false,
inputs: &[Type::Serial],
variable_inputs: 0,
decode: decode_serial,
};
pub(crate) const BITPACK_INT: Desc = Desc {
id: 28,
min_version: 3,
regens: &[Type::Numeric],
variable_regens: false,
inputs: &[Type::Serial],
variable_inputs: 0,
decode: decode_numeric,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Header {
width: usize,
bits: u32,
count: usize,
}
fn parse_header(header: &[u8], packed_size: usize) -> Result<Header, DecodeError> {
let (&first, extra) = header.split_first().ok_or_else(corrupt)?;
if extra.len() > 1 {
return Err(corrupt());
}
let width = 1usize << (first >> 6);
let bits = u32::from(first & 0x3f) + 1;
if bits as usize > width * 8 {
return Err(corrupt());
}
let extra = extra.first().map_or(0, |&n| usize::from(n));
let capacity = packed_size
.checked_mul(8)
.ok_or_else(corrupt)?
.checked_div(bits as usize)
.ok_or_else(corrupt)?;
let count = capacity.checked_sub(extra).ok_or_else(corrupt)?;
Ok(Header { width, bits, count })
}
fn decode_serial(
ctx: &mut Ctx<'_>,
inputs: &[Input<'_>],
regens: &mut [Stream],
) -> Result<(), DecodeError> {
decode(ctx, inputs, regens, true)
}
fn decode_numeric(
ctx: &mut Ctx<'_>,
inputs: &[Input<'_>],
regens: &mut [Stream],
) -> Result<(), DecodeError> {
decode(ctx, inputs, regens, false)
}
fn decode(
ctx: &mut Ctx<'_>,
inputs: &[Input<'_>],
regens: &mut [Stream],
serial: bool,
) -> Result<(), DecodeError> {
let packed = inputs[0].bytes;
let header = parse_header(ctx.header, packed.len())?;
if serial && header.width != 1 {
return Err(corrupt());
}
let out = regens[0].fixed(header.width, header.count)?;
let used = header
.count
.checked_mul(header.bits as usize)
.map(|bits| bits.div_ceil(8))
.ok_or_else(corrupt)?;
if used != packed.len() {
return Err(corrupt());
}
match header.width {
1 => unpack::<1>(out, packed, header.count, header.bits),
2 => unpack::<2>(out, packed, header.count, header.bits),
4 => unpack::<4>(out, packed, header.count, header.bits),
_ => unpack::<8>(out, packed, header.count, header.bits),
}
Ok(())
}
#[inline]
fn load_u64(src: &[u8], at: usize) -> u64 {
let rest = src.get(at..).unwrap_or_default();
match rest.first_chunk::<8>() {
Some(chunk) => u64::from_le_bytes(*chunk),
None => {
let mut buf = [0u8; 8];
buf[..rest.len()].copy_from_slice(rest);
u64::from_le_bytes(buf)
}
}
}
#[inline]
fn load_u128(src: &[u8], at: usize) -> u128 {
let rest = src.get(at..).unwrap_or_default();
match rest.first_chunk::<16>() {
Some(chunk) => u128::from_le_bytes(*chunk),
None => {
let mut buf = [0u8; 16];
buf[..rest.len()].copy_from_slice(rest);
u128::from_le_bytes(buf)
}
}
}
fn unpack<const W: usize>(out: &mut alloc::vec::Vec<u8>, src: &[u8], count: usize, bits: u32) {
if bits as usize == W * 8 {
out.extend_from_slice(src.get(..count * W).unwrap_or(src));
return;
}
let mask = if bits == 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
let step = bits as usize;
let mut position = 0usize;
for _ in 0..count {
let (byte, shift) = (position >> 3, position & 7);
let value = if bits <= 56 {
(load_u64(src, byte) >> shift) & mask
} else {
((load_u128(src, byte) >> shift) as u64) & mask
};
out.extend_from_slice(&value.to_le_bytes()[..W]);
position += step;
}
}
#[cfg(test)]
mod tests {
use super::super::State;
use super::*;
use crate::Backend;
use alloc::vec::Vec;
fn run(
desc: &Desc,
header: &[u8],
packed: &[u8],
budget: usize,
) -> Result<Stream, DecodeError> {
let mut state = State::new(Backend::default());
let mut ctx = Ctx::new(&mut state, header);
let mut regens = [Stream::default()];
regens[0].prepare(desc.regen_type(0), budget);
(desc.decode)(&mut ctx, &[Input::serial(packed)], &mut regens)?;
let [stream] = regens;
stream.validate()?;
Ok(stream)
}
fn pack(values: &[u64], bits: u32) -> Vec<u8> {
let mut out = alloc::vec![0u8; (values.len() * bits as usize).div_ceil(8)];
for (i, &value) in values.iter().enumerate() {
for b in 0..bits as usize {
if value >> b & 1 == 1 {
let at = i * bits as usize + b;
out[at / 8] |= 1 << (at % 8);
}
}
}
out
}
fn header(width: usize, bits: u32, extra: Option<u8>) -> Vec<u8> {
let mut header = alloc::vec![((width.trailing_zeros() as u8) << 6) | (bits - 1) as u8];
header.extend(extra);
header
}
fn values(width: usize, bits: u32, count: usize) -> Vec<u64> {
let mask = if bits == 64 {
u64::MAX
} else {
(1 << bits) - 1
};
let mut x = 0x9e37_79b9_7f4a_7c15u64 ^ (bits as u64) << 8 ^ width as u64;
(0..count)
.map(|_| {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x & mask
})
.collect()
}
#[test]
fn every_width_and_bit_count_round_trips() {
for width in [1usize, 2, 4, 8] {
for bits in 1..=(width as u32 * 8) {
for count in [0usize, 1, 2, 7, 8, 9, 33] {
let values = values(width, bits, count);
let packed = pack(&values, bits);
let stream = run(&BITPACK_INT, &header(width, bits, None), &packed, 1 << 12);
let Ok(stream) = stream else {
panic!("{width} {bits} {count}: {stream:?}");
};
let expected: Vec<u8> = values
.iter()
.flat_map(|v| v.to_le_bytes()[..width].to_vec())
.collect();
let padding = (packed.len() * 8 / bits as usize) - count;
if padding == 0 {
assert_eq!(stream.bytes(), expected, "{width} {bits} {count}");
assert_eq!(stream.as_input().width, width);
}
}
}
}
}
#[test]
fn extra_elements_are_dropped_from_the_end() {
let values = values(1, 5, 2);
let packed = pack(&values, 5);
assert_eq!(packed.len(), 2);
let out = run(&BITPACK_SERIAL, &header(1, 5, Some(1)), &packed, 64).unwrap();
assert_eq!(
out.bytes(),
values.iter().map(|&v| v as u8).collect::<Vec<_>>()
);
let all = run(&BITPACK_SERIAL, &header(1, 5, None), &packed, 64).unwrap();
assert_eq!(all.bytes().len(), 3);
assert!(run(&BITPACK_SERIAL, &header(1, 5, Some(2)), &packed, 64).is_err());
assert!(run(&BITPACK_SERIAL, &header(1, 5, Some(4)), &packed, 64).is_err());
}
#[test]
fn full_width_fields_are_copied() {
let packed: Vec<u8> = (0u8..32).collect();
let out = run(&BITPACK_INT, &header(8, 64, None), &packed, 64).unwrap();
assert_eq!(out.bytes(), packed);
let out = run(&BITPACK_INT, &header(4, 32, None), &packed, 64).unwrap();
assert_eq!((out.bytes(), out.as_input().width), (&packed[..], 4));
}
#[test]
fn empty_streams_decode_to_nothing() {
let out = run(&BITPACK_SERIAL, &header(1, 5, None), &[], 0).unwrap();
assert!(out.bytes().is_empty());
let out = run(&BITPACK_INT, &header(2, 9, None), &[], 0).unwrap();
assert!(out.bytes().is_empty());
}
#[test]
fn malformed_headers_and_streams_are_rejected() {
let packed = [0xffu8; 4];
for bad in [&[][..], &[0, 0, 0], &[0x07 | 0x40, 1, 2]] {
assert!(run(&BITPACK_INT, bad, &packed, 64).is_err(), "{bad:?}");
}
assert!(run(&BITPACK_INT, &[0x08], &packed, 64).is_err());
assert!(run(&BITPACK_INT, &[0x40 | 0x10], &packed, 64).is_err());
assert!(run(&BITPACK_SERIAL, &header(2, 9, None), &packed, 64).is_err());
assert!(run(&BITPACK_INT, &header(1, 8, Some(5)), &packed, 64).is_err());
assert!(run(&BITPACK_INT, &header(1, 8, Some(1)), &packed, 64).is_err());
assert!(run(&BITPACK_INT, &header(1, 8, None), &packed, 3).is_err());
}
#[test]
fn short_windows_read_zero_past_the_end() {
assert_eq!(load_u64(&[1, 2], 0), 0x0201);
assert_eq!(load_u64(&[1, 2], 5), 0);
assert_eq!(load_u128(&[1, 2, 3], 1), 0x0302);
assert_eq!(load_u128(&[0xff; 20], 2), u128::MAX);
}
}