use std::io::{Seek, Write};
use crate::{Error, Result};
use super::options::WriteFilter;
use super::{FilteredFolderInfo, Writer};
#[cfg(feature = "aes")]
use super::EncryptedFolderInfo;
impl<W: Write + Seek> Writer<W> {
pub(crate) fn compress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
use crate::codec::CodecMethod;
match self.options.method {
CodecMethod::Copy => Ok(data.to_vec()),
#[cfg(feature = "lzma2")]
CodecMethod::Lzma2 => self.compress_lzma2(data),
#[cfg(feature = "lzma")]
CodecMethod::Lzma => self.compress_lzma(data),
#[cfg(feature = "deflate")]
CodecMethod::Deflate => self.compress_deflate(data),
#[cfg(feature = "bzip2")]
CodecMethod::BZip2 => self.compress_bzip2(data),
#[cfg(feature = "zstd")]
CodecMethod::Zstd => self.compress_zstd(data),
#[cfg(feature = "lz4")]
CodecMethod::Lz4 => self.compress_lz4(data),
#[cfg(feature = "brotli")]
CodecMethod::Brotli => self.compress_brotli(data),
#[cfg(feature = "ppmd")]
CodecMethod::PPMd => self.compress_ppmd(data),
#[allow(unreachable_patterns)]
_ => Err(Error::UnsupportedMethod {
method_id: self.options.method.method_id(),
}),
}
}
pub(crate) fn filter_data(&self, data: &[u8]) -> Result<Option<Vec<u8>>> {
use crate::codec::bcj_encoders::*;
match self.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)
}
}
}
pub(crate) fn filter_and_compress_data(
&self,
data: &[u8],
) -> Result<(Vec<u8>, Option<FilteredFolderInfo>)> {
let (data_to_compress, filter_info) = if self.options.filter.is_active() {
let filtered = self.filter_data(data)?.unwrap_or_else(|| data.to_vec());
let filtered_size = filtered.len() as u64;
let info = FilteredFolderInfo {
filter_method: self.options.filter.method_id().unwrap_or(&[]).to_vec(),
filter_properties: self.options.filter.properties(),
filtered_size,
};
(filtered, Some(info))
} else {
(data.to_vec(), None)
};
let compressed = self.compress_data(&data_to_compress)?;
Ok((compressed, filter_info))
}
#[cfg(feature = "aes")]
pub(crate) fn filter_compress_and_encrypt_data(
&self,
data: &[u8],
) -> Result<(Vec<u8>, Option<FilteredFolderInfo>, EncryptedFolderInfo)> {
use crate::crypto::{Aes256Encoder, AesProperties, derive_key};
let (data_to_compress, filter_info) = if self.options.filter.is_active() {
let filtered = self.filter_data(data)?.unwrap_or_else(|| data.to_vec());
let filtered_size = filtered.len() as u64;
let info = FilteredFolderInfo {
filter_method: self.options.filter.method_id().unwrap_or(&[]).to_vec(),
filter_properties: self.options.filter.properties(),
filtered_size,
};
(filtered, Some(info))
} else {
(data.to_vec(), None)
};
let compressed = self.compress_data(&data_to_compress)?;
let compressed_size = compressed.len() as u64;
let password = self
.options
.password
.as_ref()
.ok_or_else(|| Error::InvalidFormat("encryption requires a password".into()))?;
let (salt, iv) = self.options.nonce_policy.generate()?;
let key = derive_key(
password,
&salt,
self.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).map_err(Error::Io)?;
encoder.finish().map_err(Error::Io)?;
output
};
let aes_properties =
AesProperties::encode(self.options.nonce_policy.num_cycles_power(), &salt, &iv);
Ok((
encrypted,
filter_info,
EncryptedFolderInfo {
aes_properties,
compressed_size,
},
))
}
}