#[allow(unused_imports)]
use std::io::Write;
#[allow(unused_imports)]
use crate::Result;
#[allow(unused_imports)]
use super::options::WriteOptions;
pub(crate) struct Compressed {
pub data: Vec<u8>,
pub properties: Vec<u8>,
}
impl Compressed {
pub(crate) fn without_properties(data: Vec<u8>) -> Self {
Self {
data,
properties: Vec::new(),
}
}
}
#[cfg(feature = "lzma")]
pub(crate) fn dictionary_size(options: &WriteOptions, data_len: usize) -> u32 {
use crate::codec::lzma::{dict_size_covering, preset_dict_size};
preset_dict_size(options.level).min(dict_size_covering(data_len as u64))
}
#[cfg(feature = "parallel")]
pub(crate) fn encoder_memory_usage(
options: &WriteOptions,
#[cfg_attr(not(feature = "lzma"), allow(unused_variables))] data_len: usize,
) -> u64 {
#[allow(unused_imports)]
use crate::codec::CodecMethod;
const SMALL_ENCODER: u64 = 1 << 20;
#[allow(clippy::match_single_binding)]
match options.method {
#[cfg(feature = "lzma2")]
CodecMethod::Lzma2 => crate::codec::lzma::encoder_memory_usage(
options.level,
dictionary_size(options, data_len),
),
#[cfg(feature = "lzma")]
CodecMethod::Lzma => crate::codec::lzma::encoder_memory_usage(
options.level,
dictionary_size(options, data_len),
),
#[cfg(feature = "ppmd")]
CodecMethod::PPMd => {
let (_, mem_size) = ppmd_settings(options);
u64::from(mem_size) + SMALL_ENCODER
}
#[cfg(feature = "bzip2")]
CodecMethod::BZip2 => {
let block = u64::from(options.level.clamp(1, 9)) * 100 * 1024;
block * 8 + SMALL_ENCODER
}
_ => SMALL_ENCODER,
}
}
#[cfg(feature = "lzma2")]
pub(crate) fn compress_lzma2(
options: &WriteOptions,
data: &[u8],
may_thread: bool,
) -> Result<Compressed> {
use crate::codec::lzma::{Lzma2Encoder, Lzma2EncoderOptions};
let opts = Lzma2EncoderOptions {
preset: options.level,
dict_size: Some(dictionary_size(options, data.len())),
};
#[cfg(feature = "parallel")]
if may_thread {
if let Some(compressed) = compress_lzma2_parallel(options, data, &opts)? {
return Ok(compressed);
}
}
#[cfg(not(feature = "parallel"))]
let _ = may_thread;
let mut output = Vec::new();
{
let mut encoder = Lzma2Encoder::new(&mut output, &opts);
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Compressed {
data: output,
properties: opts.properties(),
})
}
#[cfg(all(feature = "lzma2", feature = "parallel"))]
fn compress_lzma2_parallel(
options: &WriteOptions,
data: &[u8],
opts: &crate::codec::lzma::Lzma2EncoderOptions,
) -> Result<Option<Compressed>> {
use crate::codec::lzma::Lzma2EncoderMt;
if options.threads.is_single() {
return Ok(None);
}
const DICTIONARIES_PER_CHUNK: u64 = 1;
let dictionary = u64::from(opts.dict_size.unwrap_or(0));
let chunk_size = dictionary.saturating_mul(DICTIONARIES_PER_CHUNK);
if chunk_size == 0 {
return Ok(None);
}
let chunks = (data.len() as u64).div_ceil(chunk_size);
if chunks < 2 {
return Ok(None);
}
let workers = chunks
.min(super::entry_compression::workers_within_budget(options, data.len()) as u64)
.max(1);
let mut output = Vec::new();
{
let mut encoder = Lzma2EncoderMt::new(&mut output, opts, chunk_size, workers as u32)?;
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Some(Compressed {
data: output,
properties: opts.properties(),
}))
}
#[cfg(feature = "lzma")]
pub(crate) fn compress_lzma(options: &WriteOptions, data: &[u8]) -> Result<Compressed> {
use crate::codec::lzma::{LzmaEncoder, LzmaEncoderOptions};
let opts = LzmaEncoderOptions {
preset: options.level,
dict_size: Some(dictionary_size(options, data.len())),
};
let mut output = Vec::new();
{
let mut encoder = LzmaEncoder::new(&mut output, &opts)?;
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Compressed {
data: output,
properties: opts.properties(),
})
}
#[cfg(feature = "deflate")]
pub(crate) fn compress_deflate(options: &WriteOptions, data: &[u8]) -> Result<Compressed> {
use crate::codec::deflate::{DeflateEncoder, DeflateEncoderOptions};
let opts = DeflateEncoderOptions {
level: options.level,
};
let mut output = Vec::new();
{
let mut encoder = DeflateEncoder::new(&mut output, &opts);
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Compressed::without_properties(output))
}
#[cfg(feature = "bzip2")]
pub(crate) fn compress_bzip2(options: &WriteOptions, data: &[u8]) -> Result<Compressed> {
use crate::codec::bzip2::{Bzip2Encoder, Bzip2EncoderOptions};
let opts = Bzip2EncoderOptions {
level: options.level,
};
let mut output = Vec::new();
{
let mut encoder = Bzip2Encoder::new(&mut output, &opts);
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Compressed::without_properties(output))
}
#[cfg(feature = "zstd")]
pub(crate) fn compress_zstd(options: &WriteOptions, data: &[u8]) -> Result<Compressed> {
use super::ZSTD_LEVEL_MAP;
use crate::codec::zstd::{ZstdEncoderOptions, ZstdStreamEncoder};
let zstd_level = ZSTD_LEVEL_MAP[options.level.min(9) as usize];
let opts = ZstdEncoderOptions { level: zstd_level };
let mut output = Vec::new();
{
let mut encoder = ZstdStreamEncoder::new(&mut output, &opts)
.map_err(|e| crate::Error::Io(std::io::Error::other(e)))?;
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Compressed::without_properties(output))
}
#[cfg(feature = "lz4")]
pub(crate) fn compress_lz4(_options: &WriteOptions, data: &[u8]) -> Result<Compressed> {
use crate::codec::lz4::{Lz4Encoder, Lz4EncoderOptions};
let opts = Lz4EncoderOptions::default();
let mut output = Vec::new();
{
let mut encoder = Lz4Encoder::new(&mut output, &opts);
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Compressed::without_properties(output))
}
#[cfg(feature = "brotli")]
pub(crate) fn compress_brotli(options: &WriteOptions, data: &[u8]) -> Result<Compressed> {
use super::BROTLI_QUALITY_MAP;
use crate::codec::brotli::{BrotliEncoder, BrotliEncoderOptions};
let quality = BROTLI_QUALITY_MAP[options.level.min(9) as usize];
let opts = BrotliEncoderOptions {
quality,
lg_window_size: 22,
};
let mut output = Vec::new();
{
let mut encoder = BrotliEncoder::new(&mut output, &opts);
encoder.write_all(data).map_err(crate::Error::Io)?;
encoder.try_finish().map_err(crate::Error::Io)?;
}
Ok(Compressed::without_properties(output))
}
#[cfg(feature = "ppmd")]
fn ppmd_settings(options: &WriteOptions) -> (u32, u32) {
match options.level {
0..=2 => (4, 4 * 1024 * 1024),
3..=4 => (6, 8 * 1024 * 1024),
5..=6 => (6, 16 * 1024 * 1024),
7..=8 => (8, 32 * 1024 * 1024),
_ => (8, 64 * 1024 * 1024),
}
}
#[cfg(feature = "ppmd")]
pub(crate) fn compress_ppmd(options: &WriteOptions, data: &[u8]) -> Result<Compressed> {
use crate::codec::Encoder;
use crate::codec::ppmd::{PpmdEncoder, PpmdEncoderOptions};
let (order, mem_size) = ppmd_settings(options);
let opts = PpmdEncoderOptions::new(order, mem_size);
let mut output = Vec::new();
{
let mut encoder = PpmdEncoder::new(&mut output, &opts)?;
encoder.write_all(data).map_err(crate::Error::Io)?;
Box::new(encoder).finish().map_err(crate::Error::Io)?;
}
let mut properties = vec![order as u8];
properties.extend_from_slice(&mem_size.to_le_bytes());
Ok(Compressed {
data: output,
properties,
})
}