use super::{Ctx, Desc};
use crate::decompressor::core::data::{Input, Stream, Type, corrupt};
use crate::decompressor::error::DecodeError;
pub(crate) const FLOAT_DECONSTRUCT: Desc = Desc {
id: 33,
min_version: 4,
regens: &[Type::Numeric],
variable_regens: false,
inputs: &[Type::Struct, Type::Serial],
variable_inputs: 0,
decode,
};
fn decode(
ctx: &mut Ctx<'_>,
inputs: &[Input<'_>],
regens: &mut [Stream],
) -> Result<(), DecodeError> {
let (sign_fraction, exponent) = (&inputs[0], &inputs[1]);
let &[kind] = ctx.header else {
return Err(corrupt());
};
let (sign_fraction_width, width) = match kind {
0 => (3, 4),
1 => (1, 2),
2 => (2, 2),
_ => return Err(corrupt()),
};
let count = exponent.len();
if sign_fraction.len() != count
|| sign_fraction.width != sign_fraction_width
|| exponent.width != 1
{
return Err(corrupt());
}
let out = regens[0].fixed(width, count)?;
out.resize(count * width, 0);
let exponents = exponent.bytes.iter();
let parts = sign_fraction.bytes.chunks_exact(sign_fraction_width);
for ((to, &exp), part) in out.chunks_exact_mut(width).zip(exponents).zip(parts) {
match kind {
0 => {
let packed = u32::from_le_bytes([part[0], part[1], part[2], 0]);
let value = (u32::from(exp) << 23) | (packed << 31) | (packed >> 1);
to.copy_from_slice(&value.to_le_bytes());
}
1 => {
let packed = part[0];
let value =
(u16::from(packed) << 15) | (u16::from(exp) << 7) | u16::from(packed >> 1);
to.copy_from_slice(&value.to_le_bytes());
}
_ => {
let packed = u16::from_le_bytes([part[0], part[1]]);
let value = (packed << 15) | (u16::from(exp) << 10) | (packed >> 1);
to.copy_from_slice(&value.to_le_bytes());
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
const DESC: Desc = FLOAT_DECONSTRUCT;
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 le(width: usize, values: &[u64]) -> Vec<u8> {
values
.iter()
.flat_map(|value| value.to_le_bytes()[..width].to_vec())
.collect()
}
fn sign_fraction_input(bytes: &[u8], width: usize) -> Input<'_> {
Input {
ty: Type::Struct,
width,
bytes,
lens: &[],
}
}
fn rebuild(
kind: u8,
sign_fraction: &[u8],
width: usize,
exponent: &[u8],
) -> Result<Vec<u8>, DecodeError> {
let inputs = [
sign_fraction_input(sign_fraction, width),
Input::serial(exponent),
];
run(&[kind], &inputs).map(|stream| stream.bytes().to_vec())
}
#[test]
fn float32_parts_recombine() {
let packed: u32 = 1 | (0x40_0000 << 1);
let out = rebuild(0, &packed.to_le_bytes()[..3], 3, &[127]).unwrap();
assert_eq!(out, (-1.5f32).to_le_bytes());
let out = rebuild(0, &[0, 0, 0, 0, 0, 0], 3, &[1, 255]).unwrap();
assert_eq!(out, le(4, &[1 << 23, 255 << 23]));
}
#[test]
fn bfloat16_parts_recombine() {
let out = rebuild(1, &[1 | (0x40 << 1)], 1, &[0x7f]).unwrap();
assert_eq!(out, 0xbfc0u16.to_le_bytes());
}
#[test]
fn float16_parts_recombine() {
let packed: u16 = 1 | (0x200 << 1);
let out = rebuild(2, &packed.to_le_bytes(), 2, &[15]).unwrap();
assert_eq!(out, 0xbe00u16.to_le_bytes());
}
#[test]
fn empty_streams_keep_the_output_width() {
for (kind, width, out_width) in [(0u8, 3usize, 4usize), (1, 1, 2), (2, 2, 2)] {
let stream = run(
&[kind],
&[sign_fraction_input(&[], width), Input::serial(&[])],
)
.unwrap();
assert!(stream.bytes().is_empty());
assert_eq!(stream.as_input().width, out_width);
}
}
#[test]
fn malformed_headers_are_corrupt() {
let inputs = [sign_fraction_input(&[0; 3], 3), Input::serial(&[0])];
for header in [&[][..], &[3], &[0, 0], &[255]] {
assert_eq!(run(header, &inputs).err(), Some(corrupt()), "{header:?}");
}
}
#[test]
fn mismatched_streams_are_corrupt() {
assert!(rebuild(0, &[0; 3], 3, &[0, 0]).is_err());
assert!(rebuild(0, &[0; 2], 2, &[0]).is_err());
assert!(rebuild(1, &[0; 2], 2, &[0, 0]).is_err());
let wide = Input {
ty: Type::Serial,
width: 2,
bytes: &[0, 0],
lens: &[],
};
assert_eq!(
run(&[1], &[sign_fraction_input(&[0], 1), wide]).err(),
Some(corrupt())
);
}
}