use crate::codec::composite;
use crate::codec::CODEC_BLOSC2_SHUFFLE_LZ4;
pub type ShuffleLz4Codec = composite::FilterCodecComposite<omnizip_filters::shuffle::ByteShuffle>;
const MIN_SHUFFLE_SIZE: usize = 512;
#[must_use]
pub fn shuffle_lz4(item_size: usize) -> ShuffleLz4Codec {
let item_size = if [1, 2, 4, 8].contains(&item_size) {
item_size
} else {
4
};
ShuffleLz4Codec::new(
omnizip_filters::shuffle::ByteShuffle::new(item_size),
crate::codec::CODEC_LZ4,
CODEC_BLOSC2_SHUFFLE_LZ4,
"shuffle+lz4",
MIN_SHUFFLE_SIZE,
)
}
#[must_use]
pub fn float32() -> ShuffleLz4Codec {
shuffle_lz4(4)
}
#[must_use]
pub fn float64() -> ShuffleLz4Codec {
shuffle_lz4(8)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::Codec;
#[test]
fn round_trips_float32_array() {
let samples: Vec<f32> = (0..1024).map(|i| (i as f32 * 0.1).sin()).collect();
let bytes: Vec<u8> = samples.iter().flat_map(|f| f.to_le_bytes()).collect();
let codec = float32();
let compressed = codec.compress(&bytes).expect("compress");
let recovered = codec
.decompress(&compressed, bytes.len() as u32)
.expect("decompress");
assert_eq!(recovered, bytes);
}
#[test]
fn beats_plain_lz4_on_smooth_floats() {
let samples: Vec<f32> = (0..4096).map(|i| (i as f32 * 0.01).sin()).collect();
let bytes: Vec<u8> = samples.iter().flat_map(|f| f.to_le_bytes()).collect();
let codec = float32();
let shuffled_compressed = codec.compress(&bytes).expect("shuffle+lz4");
let plain = crate::codec::compress(crate::codec::CODEC_LZ4, &bytes).expect("plain lz4");
let ratio = shuffled_compressed.len() as f64 / plain.len().max(1) as f64;
assert!(
ratio <= 1.05,
"shuffle+lz4 ({}) should be within 5% of plain LZ4 ({}) on smooth floats (ratio {:.3})",
shuffled_compressed.len(),
plain.len(),
ratio
);
}
}