use alloc::vec::Vec;
use crate::Backend;
#[derive(Debug)]
pub(crate) struct ZstdNode {
encoder: mzstd::Compressor,
output: Vec<u8>,
}
const MAGIC_LEN: usize = 4;
pub(crate) fn zstd_backend(backend: Backend) -> mzstd::Backend {
mzstd::Backend::available()
.into_iter()
.find(|candidate| candidate.name() == backend.name())
.unwrap_or_default()
}
fn zstd_config(level: i32) -> mzstd::EncoderConfig {
let level = mzstd::Level::try_from(level).unwrap_or_default();
mzstd::EncoderConfig::default().with_level(level)
}
impl ZstdNode {
pub(crate) fn new(level: i32, backend: Backend) -> Self {
let encoder = mzstd::Compressor::builder(zstd_config(level))
.with_backend(zstd_backend(backend))
.build()
.unwrap_or_default();
Self {
encoder,
output: Vec::new(),
}
}
pub(crate) fn set_level(&mut self, level: i32) {
let _ = self.encoder.reconfigure(zstd_config(level));
}
#[cfg_attr(all(feature = "hotpath", not(feature = "no_std")), hotpath::measure)]
pub(crate) fn encode(&mut self, input: &[u8]) -> Option<&[u8]> {
self.output.clear();
self.encoder.compress_into(input, &mut self.output).ok()?;
let stream = self.output.get_mut(MAGIC_LEN - 1..)?;
stream[0] = 1;
Some(stream)
}
pub(crate) fn retained_bytes(&self) -> usize {
self.output.capacity() + self.encoder.retained_bytes()
}
pub(crate) fn trim(&mut self, policy: crate::RetentionPolicy) {
if !policy.allows(self.retained_bytes()) {
self.output = Vec::new();
self.encoder.trim(mzstd::RetentionPolicy::ReleaseAll);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stream_is_the_width_then_the_magicless_frame() {
let input = alloc::vec![7u8; 4096];
let mut node = ZstdNode::new(6, Backend::default());
let mut reference = mzstd::Compressor::new(zstd_config(6)).expect("zstd config");
let frame = reference.compress(&input).expect("zstd compress");
let stream = node.encode(&input).expect("encode").to_vec();
assert_eq!(stream[0], 1);
assert_eq!(&stream[1..], &frame[MAGIC_LEN..]);
}
#[test]
fn level_changes_apply_to_the_next_chunk() {
let input: alloc::vec::Vec<u8> = (0..20_000u32)
.map(|i| b"abcdefgh"[(i.wrapping_mul(2_654_435_761) >> 29) as usize])
.collect();
let mut node = ZstdNode::new(1, Backend::default());
let fast = node.encode(&input).unwrap().to_vec();
node.set_level(19);
let strong = node.encode(&input).unwrap().to_vec();
assert_ne!(fast, strong);
assert!(node.retained_bytes() > 0);
node.trim(crate::RetentionPolicy::ReleaseAll);
assert_eq!(node.output.capacity(), 0);
}
#[test]
fn backends_map_to_the_same_named_zstd_backend() {
for backend in Backend::available() {
let mapped = zstd_backend(backend);
if backend != Backend::SCALAR {
assert_eq!(mapped.name(), backend.name());
}
}
}
}