use mzstd::CParameter;
use super::FnEncoder;
use crate::compressor::core::data::Stream;
use crate::compressor::core::error::NodeError;
use crate::compressor::core::exec::NodeCx;
use crate::compressor::core::registry::dict::Prepared;
use crate::compressor::core::zstd::zstd_config;
pub(crate) static ZSTD: FnEncoder = FnEncoder(encode);
const C_FORMAT: i32 = crate::graph::params::zstd::FORMAT;
const C_CONTENT_SIZE_FLAG: i32 = crate::graph::params::zstd::CONTENT_SIZE_FLAG;
const MEMORY_USAGE_CAP_LOG_FACTOR: u32 = 3;
fn node_config(cx: &NodeCx<'_, '_>) -> Result<mzstd::EncoderConfig, NodeError> {
let mut config = zstd_config(0);
let level = CParameter::try_from((crate::graph::params::zstd::COMPRESSION_LEVEL, cx.level()))
.map_err(|_| NodeError::InvalidParam("invalid zstd compression level"))?;
config = config.with_parameter(level);
if cx.decompression_level() == 1 {
config = config.with_parameter(CParameter::LiteralCompressionMode(mzstd::Switch::Disabled));
}
for &(id, value) in cx.ints() {
if id == C_FORMAT || id == C_CONTENT_SIZE_FLAG {
return Err(NodeError::InvalidParam(
"this zstd parameter cannot be modified",
));
}
let parameter = CParameter::try_from((id, value))
.map_err(|_| NodeError::InvalidParam("zstd rejected the parameter"))?
.check_memory_cap(MEMORY_USAGE_CAP_LOG_FACTOR)
.map_err(|_| NodeError::InvalidParam("zstd memory parameter above its cap"))?;
config = config.with_parameter(parameter);
}
Ok(config)
}
fn encode<'a>(cx: &mut NodeCx<'_, 'a>, inputs: &[Stream<'a>]) -> Result<(), NodeError> {
let input = &inputs[0];
let config = node_config(cx)?;
cx.zstd
.set_config(config)
.map_err(|()| NodeError::InvalidParam("invalid zstd parameter combination"))?;
let dictionary = cx.dictionary.map(|Prepared::Zstd(dictionary)| dictionary);
let stream = cx
.zstd
.encode_elements(input.bytes(), input.width(), input.n_elts(), dictionary)
.ok_or(NodeError::Backend("zstd"))?
.to_vec();
cx.emit(0, 1, stream)?;
Ok(())
}