use super::{Ctx, Desc};
use crate::decompressor::core::data::{Input, Stream, Type, corrupt};
use crate::decompressor::error::DecodeError;
pub(crate) const BITSPLIT: Desc = Desc {
id: 63,
min_version: 24,
regens: &[Type::Numeric],
variable_regens: false,
inputs: &[],
variable_inputs: Type::Numeric.bit(),
decode,
};
const BLOCK: usize = 512;
const fn field_width(bits: u8) -> usize {
match bits {
0..=8 => 1,
9..=16 => 2,
17..=32 => 4,
_ => 8,
}
}
fn load(bytes: &[u8]) -> u64 {
let mut value = [0u8; 8];
for (to, from) in value.iter_mut().zip(bytes) {
*to = *from;
}
u64::from_le_bytes(value)
}
fn decode(
ctx: &mut Ctx<'_>,
inputs: &[Input<'_>],
regens: &mut [Stream],
) -> Result<(), DecodeError> {
let Some((&width, stored)) = ctx.header.split_first() else {
return Err(corrupt());
};
let width = usize::from(width);
if !matches!(width, 1 | 2 | 4 | 8) {
return Err(corrupt());
}
let mut sum = 0usize;
for &bits in stored {
if bits == 0 {
return Err(corrupt());
}
sum += usize::from(bits);
}
if sum > width * 8 {
return Err(corrupt());
}
let mut widths = [0u8; 64];
let count = inputs.len();
if count == stored.len() {
widths[..count].copy_from_slice(stored);
} else if count == stored.len() + 1 {
if sum == width * 8 || count > widths.len() {
return Err(corrupt());
}
widths[..stored.len()].copy_from_slice(stored);
widths[stored.len()] = (width * 8 - sum) as u8;
} else {
return Err(corrupt());
}
if count == 0 {
return Err(corrupt());
}
let elements = inputs[0].len();
let mut shift = 0u32;
let mut fields = [(&[][..], 0usize, 0u32); 64];
for (field, (input, &bits)) in fields.iter_mut().zip(inputs.iter().zip(&widths)) {
if input.len() != elements
|| usize::from(bits) > input.width * 8
|| input.width != field_width(bits)
{
return Err(corrupt());
}
*field = (input.bytes, input.width, shift);
shift += u32::from(bits);
}
let out = regens[0].fixed(width, elements)?;
out.resize(elements * width, 0);
let fields = &fields[..count];
let mut joined = [0u64; BLOCK];
for (first, block) in (0..elements)
.step_by(BLOCK)
.zip(out.chunks_mut(BLOCK * width))
{
let len = block.len() / width;
let joined = &mut joined[..len];
joined.fill(0);
for &(bytes, size, shift) in fields {
let bytes = &bytes[first * size..(first + len) * size];
for (acc, part) in joined.iter_mut().zip(bytes.chunks_exact(size)) {
*acc |= load(part) << shift;
}
}
for (to, value) in block.chunks_exact_mut(width).zip(joined.iter()) {
to.copy_from_slice(&value.to_le_bytes()[..width]);
}
}
Ok(())
}
#[cfg(test)]
mod tests {
const DESC: Desc = BITSPLIT;
use super::super::State;
use super::*;
use crate::Backend;
use alloc::vec::Vec;
fn run(header: &[u8], inputs: &[Input<'_>]) -> Result<Stream, DecodeError> {
run_with(&DESC, header, inputs)
}
fn run_with(desc: &Desc, header: &[u8], inputs: &[Input<'_>]) -> 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), 1 << 16);
(desc.decode)(&mut ctx, inputs, &mut regens)?;
let [stream] = regens;
stream.validate()?;
Ok(stream)
}
fn num(width: usize, bytes: &[u8]) -> Input<'_> {
Input {
ty: Type::Numeric,
width,
bytes,
lens: &[],
}
}
fn le(width: usize, values: &[u64]) -> Vec<u8> {
values
.iter()
.flat_map(|value| value.to_le_bytes()[..width].to_vec())
.collect()
}
#[test]
fn full_coverage_infers_the_last_width() {
let low = [0x7fu8, 0x01];
let middle = [0xffu8, 0x02];
let top = [1u8, 0];
let out = run(&[2, 7, 8], &[num(1, &low), num(1, &middle), num(1, &top)]).unwrap();
assert_eq!(out.bytes(), le(2, &[0xffff, 0x0101 | (0x02 << 7)]));
assert_eq!(out.as_input().width, 2);
}
#[test]
fn partial_coverage_leaves_the_top_bits_zero() {
let low = le(2, &[0x1ff, 0x3]);
let high = [0x3fu8, 0x01];
let out = run(&[4, 10, 6], &[num(2, &low), num(1, &high)]).unwrap();
assert_eq!(out.bytes(), le(4, &[0x1ff | (0x3f << 10), 0x3 | (1 << 10)]));
}
#[test]
fn every_field_width_class_joins() {
let wide = le(8, &[0x000f_ffff_ffff_ffff]);
let mid = le(2, &[0x7ff]);
let top = [1u8];
let out = run(&[8, 52, 11], &[num(8, &wide), num(2, &mid), num(1, &top)]).unwrap();
assert_eq!(out.bytes(), le(8, &[u64::MAX]));
let thirds = le(4, &[0x7f_ffff]);
let out = run(
&[4, 23, 8],
&[num(4, &thirds), num(1, &[0xff]), num(1, &[1])],
)
.unwrap();
assert_eq!(out.bytes(), le(4, &[u64::from(u32::MAX)]));
}
#[test]
fn unmasked_fields_spill_into_the_next_one_like_the_reference() {
let out = run(&[1, 4], &[num(1, &[0xff]), num(1, &[0])]).unwrap();
assert_eq!(out.bytes(), [0xff]);
let out = run(&[1, 4], &[num(1, &[0x1f]), num(1, &[0x01])]).unwrap();
assert_eq!(out.bytes(), [0x1f | 0x10]);
}
#[test]
fn blocks_join_across_the_batch_boundary() {
let count = BLOCK * 2 + 3;
let low: Vec<u8> = (0..count).map(|i| i as u8 & 0xf).collect();
let high: Vec<u8> = (0..count).map(|i| (i >> 4) as u8 & 0xf).collect();
let out = run(&[1, 4, 4], &[num(1, &low), num(1, &high)]).unwrap();
let expected: Vec<u8> = (0..count)
.map(|i| (i as u8 & 0xf) | (((i >> 4) as u8 & 0xf) << 4))
.collect();
assert_eq!(out.bytes(), expected);
}
#[test]
fn empty_streams_keep_the_output_width() {
let out = run(
&[4, 8, 8, 8],
&[num(1, &[]), num(1, &[]), num(1, &[]), num(1, &[])],
)
.unwrap();
assert!(out.bytes().is_empty());
assert_eq!(out.as_input().width, 4);
}
#[test]
fn malformed_headers_are_corrupt() {
let one = [num(1, &[0])];
for header in [&[][..], &[3, 4], &[0, 4], &[1, 0], &[1, 9], &[1, 4, 4, 1]] {
assert_eq!(run(header, &one).err(), Some(corrupt()), "{header:?}");
}
}
#[test]
fn stream_counts_must_match_the_widths() {
let streams = [num(1, &[0]), num(1, &[0]), num(1, &[0])];
assert!(run(&[2, 4, 4], &streams[..2]).is_ok());
assert_eq!(run(&[2, 4, 4], &streams[..1]).err(), Some(corrupt()));
assert_eq!(run(&[1, 4, 4], &streams).err(), Some(corrupt()));
assert_eq!(run(&[1, 4, 4], &[]).err(), Some(corrupt()));
assert_eq!(run(&[1], &[]).err(), Some(corrupt()));
assert_eq!(run(&[1], &streams[..2]).err(), Some(corrupt()));
let many = alloc::vec![num(1, &[1]); 64];
let mut header = alloc::vec![8u8];
header.extend([1; 63]);
assert!(run(&header, &many).is_ok());
assert!(run(&header, &many[..63]).is_ok());
let mut wide = alloc::vec![8u8];
wide.extend([1; 64]);
assert!(run(&wide, &many).is_ok());
assert_eq!(
run(&wide, &alloc::vec![num(1, &[1]); 65]).err(),
Some(corrupt())
);
}
#[test]
fn stream_shapes_must_match_the_fields() {
assert_eq!(
run(&[2, 8], &[num(1, &[1, 2]), num(1, &[1])]).err(),
Some(corrupt())
);
assert_eq!(run(&[2, 12], &[num(1, &[1])]).err(), Some(corrupt()));
assert_eq!(run(&[2, 4], &[num(2, &le(2, &[1]))]).err(), Some(corrupt()));
assert_eq!(
run(&[2, 12, 4], &[num(1, &[1]), num(1, &[1])]).err(),
Some(corrupt())
);
}
#[test]
fn field_widths_follow_the_bit_counts() {
assert_eq!(
[1u8, 8, 9, 16, 17, 32, 33, 64].map(field_width),
[1, 1, 2, 2, 4, 4, 8, 8]
);
}
}