mod delta;
mod standard;
use std::slice::from_raw_parts;
pub use delta::encode_delta;
pub use standard::encode_standard;
use crate::{
bitpack::packed_byte_size,
constants::{EXC_COUNT_LEN, HEADER_LEN, MIN_HEADER_LEN},
delta::{delta_range, eval_delta_benefit},
float::AlpFloat,
params::pack_params,
sampler::{BestParams, find_best_params, find_identical_base},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Exception<R> {
pub pos: u16,
pub bits: R,
}
pub fn compress_into<F: AlpFloat>(data: &[F], dst: &mut Vec<u8>) {
compress_impl(data, dst, false);
}
pub fn compress_delta_into<F: AlpFloat>(data: &[F], dst: &mut Vec<u8>) {
compress_impl(data, dst, true);
}
fn compress_impl<F: AlpFloat>(data: &[F], dst: &mut Vec<u8>, force_delta: bool) {
let count = data.len().min(u16::MAX as usize) as u16;
if count == 0 {
dst.reserve(MIN_HEADER_LEN);
let count_bytes = 0u16.to_le_bytes();
let header = [F::TYPE_BYTE, count_bytes[0], count_bytes[1]];
dst.extend_from_slice(&header);
return;
}
let slice = &data[..count as usize];
let first = slice[0];
if slice.iter().all(|&v| v.is_exact_same(first))
&& let Some((exp, base)) = find_identical_base(first)
{
let total_needed = HEADER_LEN + F::BASE_SIZE;
dst.reserve(total_needed);
let count_bytes = count.to_le_bytes();
let params_bytes = pack_params(exp, 0, 0).to_le_bytes();
let header = [
F::TYPE_BYTE,
count_bytes[0],
count_bytes[1],
params_bytes[0],
params_bytes[1],
];
dst.extend_from_slice(&header);
F::write_base(base, dst);
return;
}
let BestParams { exp, fac, use_div } = find_best_params(slice);
let exp_factor = F::exp_factor(exp, fac);
let fac_int = F::fac_int(fac);
let frac_exp = F::frac_exp(exp);
let mut encoded_ints: Vec<F::Int> = Vec::with_capacity(slice.len());
let mut exceptions = Vec::new();
let mut min_val = F::MAX_INT;
let mut max_val = F::MIN_INT;
unsafe {
let enc_ptr: *mut F::Int = encoded_ints.as_mut_ptr();
if use_div {
for (i, &val) in slice.iter().enumerate() {
if let Some(enc) = val.try_encode_div(exp_factor) {
enc_ptr.add(i).write(enc);
min_val = min_val.min(enc);
max_val = max_val.max(enc);
} else {
enc_ptr.add(i).write(F::ZERO_INT);
exceptions.push(Exception {
pos: i as u16,
bits: val.to_raw_bits(),
});
}
}
} else if fac_int == 1 {
for (i, &val) in slice.iter().enumerate() {
let enc = val.fast_round_to_int(exp_factor);
let decoded = F::decode_from_int(enc, 1, frac_exp);
if decoded.to_raw_bits() == val.to_raw_bits() {
enc_ptr.add(i).write(enc);
min_val = min_val.min(enc);
max_val = max_val.max(enc);
} else {
enc_ptr.add(i).write(F::ZERO_INT);
exceptions.push(Exception {
pos: i as u16,
bits: val.to_raw_bits(),
});
}
}
} else {
for (i, &val) in slice.iter().enumerate() {
match val.try_encode_fast(exp_factor, fac_int, frac_exp) {
Some(enc) => {
enc_ptr.add(i).write(enc);
min_val = min_val.min(enc);
max_val = max_val.max(enc);
}
None => {
enc_ptr.add(i).write(F::ZERO_INT);
exceptions.push(Exception {
pos: i as u16,
bits: val.to_raw_bits(),
});
}
}
}
}
encoded_ints.set_len(slice.len());
}
let base = if min_val <= max_val {
min_val
} else {
F::ZERO_INT
};
let max_offset = if min_val <= max_val {
F::calc_range(min_val, max_val)
} else {
0
};
if !exceptions.is_empty() {
for exc in &exceptions {
let patch_val = if exc.pos > 0 {
unsafe { *encoded_ints.get_unchecked(exc.pos as usize - 1) }
} else {
base
};
unsafe {
*encoded_ints.get_unchecked_mut(exc.pos as usize) = patch_val;
}
}
}
let for_bit_width = F::bits_needed(max_offset);
let for_packed_len = packed_byte_size(slice.len(), for_bit_width);
let exc_len = if exceptions.is_empty() {
0
} else {
EXC_COUNT_LEN + exceptions.len() * F::EXC_ENTRY_SIZE
};
let delta_decision = if slice.len() > 1 {
let first = encoded_ints[0];
let rest = &encoded_ints[1..];
if force_delta {
Some(delta_range::<F>(first, rest))
} else {
eval_delta_benefit::<F>(first, rest, for_bit_width)
}
} else {
None
};
let (use_delta, min_delta, delta_bit_width, total_needed) = match delta_decision {
Some((min_d, delta_bw)) => {
let delta_packed_len = packed_byte_size(slice.len() - 1, delta_bw);
let delta_total = HEADER_LEN + F::BASE_SIZE * 2 + delta_packed_len + exc_len;
let for_total = HEADER_LEN + F::BASE_SIZE + for_packed_len + exc_len;
if delta_total < for_total || force_delta {
(true, min_d, delta_bw, delta_total)
} else {
(false, F::ZERO_INT, 0, for_total)
}
}
None => {
let for_total = HEADER_LEN + F::BASE_SIZE + for_packed_len + exc_len;
(false, F::ZERO_INT, 0, for_total)
}
};
let raw_len = size_of_val(slice);
if total_needed >= raw_len + MIN_HEADER_LEN {
let total_raw = MIN_HEADER_LEN + raw_len;
dst.reserve(total_raw);
let count_bytes = count.to_le_bytes();
dst.extend_from_slice(&[F::TYPE_RAW_BYTE, count_bytes[0], count_bytes[1]]);
let raw_slice = unsafe { from_raw_parts(slice.as_ptr().cast::<u8>(), raw_len) };
dst.extend_from_slice(raw_slice);
return;
}
dst.reserve(total_needed);
if use_delta {
encode_delta::<F>(
count,
exp,
fac,
use_div,
&mut encoded_ints,
min_delta,
delta_bit_width,
&exceptions,
dst,
);
} else {
encode_standard::<F>(
count,
exp,
fac,
use_div,
&encoded_ints,
base,
for_bit_width,
&exceptions,
dst,
);
}
}
#[inline]
pub fn compress<F: AlpFloat>(data: &[F]) -> Vec<u8> {
let mut dst = Vec::new();
compress_into(data, &mut dst);
dst
}
#[inline]
pub fn compress_delta<F: AlpFloat>(data: &[F]) -> Vec<u8> {
let mut dst = Vec::new();
compress_delta_into(data, &mut dst);
dst
}