use std::borrow::Cow;
use std::io::Write;
use crate::{Error, Result};
#[cfg(feature = "aes")]
use super::EncryptedFolderInfo;
use super::FilteredFolderInfo;
#[allow(unused_imports)]
use super::codecs::{self, Compressed};
use super::options::{WriteFilter, WriteOptions};
pub(crate) fn compress_data(
options: &WriteOptions,
data: &[u8],
#[cfg_attr(not(feature = "lzma2"), allow(unused_variables))] concurrency: codecs::Concurrency,
) -> Result<Compressed> {
use crate::codec::CodecMethod;
match options.method {
CodecMethod::Copy => Ok(Compressed::without_properties(data.to_vec())),
#[cfg(feature = "lzma2")]
CodecMethod::Lzma2 => codecs::compress_lzma2(options, data, concurrency),
#[cfg(feature = "lzma")]
CodecMethod::Lzma => codecs::compress_lzma(options, data),
#[cfg(feature = "deflate")]
CodecMethod::Deflate => codecs::compress_deflate(options, data),
#[cfg(feature = "bzip2")]
CodecMethod::BZip2 => codecs::compress_bzip2(options, data),
#[cfg(feature = "zstd")]
CodecMethod::Zstd => codecs::compress_zstd(options, data),
#[cfg(feature = "lz4")]
CodecMethod::Lz4 => codecs::compress_lz4(options, data),
#[cfg(feature = "brotli")]
CodecMethod::Brotli => codecs::compress_brotli(options, data),
#[cfg(feature = "ppmd")]
CodecMethod::PPMd => codecs::compress_ppmd(options, data),
#[allow(unreachable_patterns)]
_ => Err(Error::UnsupportedMethod {
method_id: options.method.method_id(),
}),
}
}
pub(crate) fn filter_data(options: &WriteOptions, data: &[u8]) -> Result<Option<Vec<u8>>> {
use crate::codec::bcj_encoders::*;
match options.filter {
WriteFilter::None => Ok(None),
WriteFilter::BcjX86 => {
let mut output = Vec::new();
let mut encoder = BcjX86Encoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::BcjArm => {
let mut output = Vec::new();
let mut encoder = BcjArmEncoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::BcjArm64 => {
let mut output = Vec::new();
let mut encoder = BcjArm64Encoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::BcjArmThumb => {
let mut output = Vec::new();
let mut encoder = BcjArmThumbEncoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::BcjPpc => {
let mut output = Vec::new();
let mut encoder = BcjPpcEncoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::BcjSparc => {
let mut output = Vec::new();
let mut encoder = BcjSparcEncoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::BcjIa64 => {
let mut output = Vec::new();
let mut encoder = BcjIa64Encoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::BcjRiscv => {
let mut output = Vec::new();
let mut encoder = BcjRiscvEncoder::new(&mut output);
encoder.write_all(data).map_err(Error::Io)?;
encoder.try_finish().map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::Delta { distance } => {
let mut output = Vec::new();
let mut encoder = DeltaEncoder::new(&mut output, distance);
encoder.write_all(data).map_err(Error::Io)?;
Ok(Some(output))
}
WriteFilter::Bcj2 => {
Ok(None)
}
}
}
fn apply_filter<'a>(
options: &WriteOptions,
data: &'a [u8],
) -> Result<(Cow<'a, [u8]>, Option<FilteredFolderInfo>)> {
if !options.filter.is_active() {
return Ok((Cow::Borrowed(data), None));
}
let filtered = match filter_data(options, data)? {
Some(filtered) => Cow::Owned(filtered),
None => Cow::Borrowed(data),
};
let info = FilteredFolderInfo {
filter_method: options.filter.method_id().unwrap_or(&[]).to_vec(),
filter_properties: options.filter.properties(),
filtered_size: filtered.len() as u64,
};
Ok((filtered, Some(info)))
}
pub(crate) fn filter_and_compress_data(
options: &WriteOptions,
data: &[u8],
concurrency: codecs::Concurrency,
) -> Result<(Compressed, Option<FilteredFolderInfo>)> {
let (data_to_compress, filter_info) = apply_filter(options, data)?;
let compressed = compress_data(options, &data_to_compress, concurrency)?;
Ok((compressed, filter_info))
}
impl<W: std::io::Write + std::io::Seek> super::Writer<W> {
#[cfg(feature = "aes")]
pub(crate) fn encrypt_compressed_with(
&mut self,
compressed: Compressed,
options: &super::options::WriteOptions,
) -> Result<(Compressed, EncryptedFolderInfo)> {
use crate::crypto::{Aes256Encoder, AesProperties, derive_key_cached};
let compressed_size = compressed.data.len() as u64;
let password = options
.password
.clone()
.ok_or_else(|| Error::InvalidFormat("encryption requires a password".into()))?;
self.hold_password(&password)?;
let (salt, iv) = self.nonce_for_stream_under(&options.nonce_policy)?;
let key = derive_key_cached(&password, &salt, options.nonce_policy.num_cycles_power())?;
let encrypted = {
let mut output = Vec::new();
let mut encoder = Aes256Encoder::with_key_iv(&mut output, key, iv);
encoder.write_all(&compressed.data).map_err(Error::Io)?;
encoder.finish().map_err(Error::Io)?;
output
};
let aes_properties =
AesProperties::encode(options.nonce_policy.num_cycles_power(), &salt, &iv)?;
Ok((
Compressed {
data: encrypted,
properties: compressed.properties,
},
EncryptedFolderInfo {
aes_properties,
compressed_size,
},
))
}
}