use alloc::vec::Vec;
use super::FnEncoder;
use crate::compressor::core::data::Stream;
use crate::compressor::core::error::NodeError;
use crate::compressor::core::exec::NodeCx;
pub(crate) static QUANTIZE_OFFSETS: FnEncoder =
FnEncoder(|cx, inputs| quantize(cx, &inputs[0], &OFFSETS));
pub(crate) static QUANTIZE_LENGTHS: FnEncoder =
FnEncoder(|cx, inputs| quantize(cx, &inputs[0], &LENGTHS));
struct Scheme {
max_pow2: u32,
delta: u32,
value_to_code: &'static [u8],
bits: fn(u8) -> u32,
}
static OFFSETS: Scheme = Scheme {
max_pow2: 0,
delta: 0,
value_to_code: &[0],
bits: offset_bits,
};
fn offset_bits(code: u8) -> u32 {
u32::from(code)
}
fn length_bits(code: u8) -> u32 {
if code < 16 { 0 } else { u32::from(code) - 12 }
}
static LENGTHS: Scheme = Scheme {
max_pow2: 16,
delta: 12,
value_to_code: &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
bits: length_bits,
};
impl Scheme {
fn code(&self, value: u32) -> u8 {
if value >= self.max_pow2 {
(value.ilog2() + self.delta) as u8
} else {
self.value_to_code[value as usize]
}
}
}
fn quantize<'a>(
cx: &mut NodeCx<'_, 'a>,
input: &Stream<'a>,
scheme: &Scheme,
) -> Result<(), NodeError> {
if input.width() != 4 {
return Err(NodeError::InvalidInput("quantize needs 32-bit numbers"));
}
let count = input.n_elts();
let mut codes = Vec::with_capacity(count);
let mut bits = Vec::with_capacity(count);
let (mut buffer, mut filled) = (0u64, 0u32);
for chunk in input.bytes().as_chunks::<4>().0 {
let value = u32::from_le_bytes(*chunk);
if scheme.max_pow2 == 0 && value == 0 {
return Err(NodeError::InvalidInput("a quantized offset cannot be 0"));
}
let code = scheme.code(value);
codes.push(code);
let extra = (scheme.bits)(code);
buffer |= u64::from(value & ((1u64 << extra) - 1) as u32) << filled;
filled += extra;
while filled >= 8 {
bits.push(buffer as u8);
buffer >>= 8;
filled -= 8;
}
}
if filled > 0 {
bits.push(buffer as u8);
}
cx.emit(0, 1, codes)?;
cx.emit(1, 1, bits)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compressor::core::codecs::test_util::run_node;
use crate::compressor::core::data::{Bytes, Type};
fn numbers(values: &[u32], width: usize) -> Stream<'static> {
let bytes = values
.iter()
.flat_map(|&v| u64::from(v).to_le_bytes()[..width].to_vec())
.collect::<Vec<u8>>();
Stream::new(Type::Numeric, width, Bytes::from(bytes))
}
#[test]
fn offsets_use_one_code_per_power_of_two() {
let out = run_node(43, &[], 3, &[numbers(&[1, 2, 3, 4, 255, 1 << 31], 4)]).unwrap();
assert_eq!(out.singles[0].bytes(), [0, 1, 1, 2, 7, 31]);
let mut expected = 0u64;
let mut at = 0;
for (value, bits) in [(0u64, 1), (1, 1), (0, 2), (127, 7), (0, 31)] {
expected |= value << at;
at += bits;
}
assert_eq!(
out.singles[1].bytes(),
&expected.to_le_bytes()[..(at as usize).div_ceil(8)]
);
}
#[test]
fn lengths_keep_small_values_and_quantize_the_rest() {
let out = run_node(44, &[], 3, &[numbers(&[0, 15, 16, 31, 32, 1 << 31], 4)]).unwrap();
assert_eq!(out.singles[0].bytes(), [0, 15, 16, 16, 17, 43]);
assert_eq!(
out.singles[1].content_size(),
(4 + 4 + 5 + 31usize).div_ceil(8)
);
}
#[test]
fn empty_input_gives_empty_streams() {
for node in [43, 44] {
let out = run_node(node, &[], 3, &[numbers(&[], 4)]).unwrap();
assert_eq!(out.singles[0].content_size(), 0);
assert_eq!(out.singles[1].content_size(), 0);
}
}
#[test]
fn a_zero_offset_and_other_widths_are_rejected() {
assert!(matches!(
run_node(43, &[], 3, &[numbers(&[3, 0], 4)]),
Err(NodeError::InvalidInput(_))
));
assert!(run_node(44, &[], 3, &[numbers(&[0], 4)]).is_ok());
for width in [1, 2, 8] {
for node in [43, 44] {
assert!(matches!(
run_node(node, &[], 3, &[numbers(&[1], width)]),
Err(NodeError::InvalidInput(_))
));
}
}
}
}