diff --git a/crates/async-compression/Cargo.toml b/crates/async-compression/Cargo.toml index f85c185b..165a651e 100644 --- a/crates/async-compression/Cargo.toml +++ b/crates/async-compression/Cargo.toml @@ -25,6 +25,7 @@ all-algorithms = [ "gzip", "lz4", "lzma", + "ppmd", "xz", "xz-parallel", "zlib", @@ -39,6 +40,7 @@ deflate64 = ["compression-codecs/deflate64"] gzip = ["compression-codecs/gzip"] lz4 = ["compression-codecs/lz4"] lzma = ["compression-codecs/lzma"] +ppmd = ["compression-codecs/ppmd"] xz = ["compression-codecs/xz", "lzma"] xz-parallel = ["compression-codecs/xz-parallel", "xz"] xz2 = ["compression-codecs/xz2", "xz"] @@ -83,6 +85,7 @@ lz4 = "1.28.1" liblzma = "0.4.2" zstd-safe = { version = "7", default-features = false } deflate64 = "0.1.5" +ppmd-rust = "1.3.0" [lints] workspace = true @@ -111,6 +114,10 @@ required-features = ["lz4"] name = "lzma" required-features = ["lzma"] +[[test]] +name = "ppmd" +required-features = ["ppmd"] + [[test]] name = "xz" required-features = ["xz"] @@ -142,3 +149,7 @@ required-features = ["zstd", "gzip", "tokio"] [[example]] name = "lzma_filters" required-features = ["xz", "tokio"] + +[[example]] +name = "ppmd_tokio_write" +required-features = ["ppmd", "tokio"] diff --git a/crates/async-compression/examples/ppmd_tokio_write.rs b/crates/async-compression/examples/ppmd_tokio_write.rs new file mode 100644 index 00000000..78fdd161 --- /dev/null +++ b/crates/async-compression/examples/ppmd_tokio_write.rs @@ -0,0 +1,29 @@ +use std::io::Result; + +use async_compression::tokio::write::{PpmdDecoder, PpmdEncoder}; +use async_compression::Level; +use tokio::io::AsyncWriteExt as _; // for `write_all` and `shutdown` + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + let data = b"example-ppmd"; + let compressed_data = compress(data).await?; + let de_compressed_data = decompress(&compressed_data).await?; + assert_eq!(de_compressed_data, data); + println!("{}", String::from_utf8(de_compressed_data).unwrap()); + Ok(()) +} + +async fn compress(in_data: &[u8]) -> Result> { + let mut encoder = PpmdEncoder::with_quality(Vec::new(), Level::Fastest); + encoder.write_all(in_data).await?; + encoder.shutdown().await?; + Ok(encoder.into_inner()) +} + +async fn decompress(in_data: &[u8]) -> Result> { + let mut decoder = PpmdDecoder::new(Vec::new()); + decoder.write_all(in_data).await?; + decoder.shutdown().await?; + Ok(decoder.into_inner()) +} diff --git a/crates/async-compression/src/macros.rs b/crates/async-compression/src/macros.rs index 75af23cc..b6f5ae14 100644 --- a/crates/async-compression/src/macros.rs +++ b/crates/async-compression/src/macros.rs @@ -375,5 +375,20 @@ macro_rules! algos { { @dec } ); + algos!(@algo ppmd ["ppmd"] PpmdDecoder PpmdEncoder <$inner> + { @enc + + pub fn with_quality(inner: $inner, level: crate::core::Level) -> Self { + Self { + inner: crate::$($mod::)+generic::Encoder::new( + inner, + crate::codecs::PpmdEncoder::new(level), + ), + } + } + } + { @dec } + ); + } } diff --git a/crates/async-compression/tests/ppmd.rs b/crates/async-compression/tests/ppmd.rs new file mode 100644 index 00000000..69b4edb7 --- /dev/null +++ b/crates/async-compression/tests/ppmd.rs @@ -0,0 +1,4 @@ +#[macro_use] +mod utils; + +test_cases!(ppmd); diff --git a/crates/async-compression/tests/utils/algos.rs b/crates/async-compression/tests/utils/algos.rs index 634c47dd..bbe92424 100644 --- a/crates/async-compression/tests/utils/algos.rs +++ b/crates/async-compression/tests/utils/algos.rs @@ -229,6 +229,27 @@ algos! { } } } + + pub mod ppmd("ppmd", PpmdEncoder, PpmdDecoder) { + pub mod sync { + pub use crate::utils::impls::sync::to_vec; + + pub fn compress(bytes: &[u8]) -> Vec { + use std::io::Write; + let mut out = Vec::new(); + let mut enc = ppmd_rust::Ppmd7Encoder::new(&mut out, 8, 4 << 20).unwrap(); + enc.write_all(bytes).unwrap(); + let _ = enc.finish(true).unwrap(); + out + } + + pub fn decompress(bytes: &[u8]) -> Vec { + use std::io::Read; + let mut dec = ppmd_rust::Ppmd7Decoder::new(bytes, 8, 4 << 20).unwrap(); + to_vec(&mut dec) + } + } + } } macro_rules! io_algo_parallel { diff --git a/crates/compression-codecs/Cargo.toml b/crates/compression-codecs/Cargo.toml index a8142f74..08ca5852 100644 --- a/crates/compression-codecs/Cargo.toml +++ b/crates/compression-codecs/Cargo.toml @@ -23,6 +23,7 @@ all-algorithms = [ "gzip", "lz4", "lzma", + "ppmd", "xz-parallel", "xz", "zlib", @@ -35,6 +36,7 @@ deflate = ["flate2"] gzip = ["flate2", "memchr"] lz4 = ["dep:lz4"] lzma = ["dep:liblzma"] +ppmd = ["dep:ppmd_rust"] xz = ["lzma"] xz-parallel = ["xz", "liblzma/parallel"] xz2 = ["xz"] @@ -55,6 +57,7 @@ libzstd = { package = "zstd", version = "0.13.1", optional = true, default-featu lz4 = { version = "1.28.1", optional = true } liblzma = { version = "0.4.5", optional = true } memchr = { version = "2", optional = true } +ppmd_rust = { package = "ppmd-rust", version = "1.3.0", optional = true } zstd-safe = { version = "7", optional = true, default-features = false } [lints] diff --git a/crates/compression-codecs/src/lib.rs b/crates/compression-codecs/src/lib.rs index 32c186a9..2a72bef8 100644 --- a/crates/compression-codecs/src/lib.rs +++ b/crates/compression-codecs/src/lib.rs @@ -22,6 +22,8 @@ pub mod gzip; pub mod lz4; #[cfg(feature = "lzma")] pub mod lzma; +#[cfg(feature = "ppmd")] +pub mod ppmd; #[cfg(feature = "xz")] pub mod xz; #[cfg(feature = "lzma")] @@ -49,6 +51,8 @@ pub use self::gzip::{GzipDecoder, GzipEncoder}; pub use self::lz4::{Lz4Decoder, Lz4Encoder}; #[cfg(feature = "lzma")] pub use self::lzma::{LzmaDecoder, LzmaEncoder}; +#[cfg(feature = "ppmd")] +pub use self::ppmd::{PpmdDecoder, PpmdEncoder}; #[cfg(feature = "xz")] pub use self::xz::{XzDecoder, XzEncoder}; #[cfg(feature = "lzma")] diff --git a/crates/compression-codecs/src/ppmd/decoder.rs b/crates/compression-codecs/src/ppmd/decoder.rs new file mode 100644 index 00000000..0f9aff61 --- /dev/null +++ b/crates/compression-codecs/src/ppmd/decoder.rs @@ -0,0 +1,68 @@ +use crate::{DecodeV2, DecodedSize}; +use compression_core::util::{PartialBuffer, WriteBuffer}; +use std::io; + +use super::params::PpmdDecoderParams; + +#[derive(Default)] +pub struct PpmdDecoder { + order: u32, + memory_size: u32, +} + +impl PpmdDecoder { + pub fn with_params(params: PpmdDecoderParams) -> Self { + Self { + order: params.order, + memory_size: params.memory_size, + } + } + + pub fn default_params() -> PpmdDecoderParams { + PpmdDecoderParams { + order: 8, + memory_size: 4 << 20, + } + } + + pub fn new() -> Self { + Self::with_params(Self::default_params()) + } +} + +impl std::fmt::Debug for PpmdDecoder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PpmdDecoder") + .field("order", &self.order) + .field("memory_size", &self.memory_size) + .finish() + } +} + +impl DecodeV2 for PpmdDecoder { + fn reinit(&mut self) -> io::Result<()> { + todo!() + } + + fn decode( + &mut self, + _input: &mut PartialBuffer<&[u8]>, + _output: &mut WriteBuffer<'_>, + ) -> io::Result { + todo!() + } + + fn flush(&mut self, _output: &mut WriteBuffer<'_>) -> io::Result { + todo!() + } + + fn finish(&mut self, _output: &mut WriteBuffer<'_>) -> io::Result { + todo!() + } +} + +impl DecodedSize for PpmdDecoder { + fn decoded_size(_input: &[u8]) -> io::Result { + todo!() + } +} diff --git a/crates/compression-codecs/src/ppmd/encoder.rs b/crates/compression-codecs/src/ppmd/encoder.rs new file mode 100644 index 00000000..898ba537 --- /dev/null +++ b/crates/compression-codecs/src/ppmd/encoder.rs @@ -0,0 +1,40 @@ +use crate::EncodeV2; +use compression_core::util::{PartialBuffer, WriteBuffer}; +use std::io; + +use super::params::PpmdEncoderParams; + +#[derive(Debug)] +pub struct PpmdEncoder { + order: u32, + memory_size: u32, + end_marker: bool, +} + +impl PpmdEncoder { + pub fn from_params(params: PpmdEncoderParams) -> Self { + Self { + order: params.order, + memory_size: params.memory_size, + end_marker: params.end_marker, + } + } +} + +impl EncodeV2 for PpmdEncoder { + fn encode( + &mut self, + _input: &mut PartialBuffer<&[u8]>, + _output: &mut WriteBuffer<'_>, + ) -> io::Result<()> { + todo!() + } + + fn flush(&mut self, _output: &mut WriteBuffer<'_>) -> io::Result { + todo!() + } + + fn finish(&mut self, _output: &mut WriteBuffer<'_>) -> io::Result { + todo!() + } +} diff --git a/crates/compression-codecs/src/ppmd/mod.rs b/crates/compression-codecs/src/ppmd/mod.rs new file mode 100644 index 00000000..c06e973d --- /dev/null +++ b/crates/compression-codecs/src/ppmd/mod.rs @@ -0,0 +1,14 @@ +use compression_core::Level; + +mod decoder; +mod encoder; +pub mod params; + +pub use decoder::PpmdDecoder; +pub use encoder::PpmdEncoder; + +impl PpmdEncoder { + pub fn new(level: Level) -> Self { + Self::from_params(params::PpmdEncoderParams::from(level)) + } +} diff --git a/crates/compression-codecs/src/ppmd/params.rs b/crates/compression-codecs/src/ppmd/params.rs new file mode 100644 index 00000000..380874b0 --- /dev/null +++ b/crates/compression-codecs/src/ppmd/params.rs @@ -0,0 +1,20 @@ +use compression_core::Level; + +#[derive(Debug, Clone, Copy)] +pub struct PpmdEncoderParams { + pub order: u32, + pub memory_size: u32, + pub end_marker: bool, +} + +#[derive(Debug, Clone, Copy)] +pub struct PpmdDecoderParams { + pub order: u32, + pub memory_size: u32, +} + +impl From for PpmdEncoderParams { + fn from(_value: Level) -> Self { + todo!() + } +} diff --git a/deny.toml b/deny.toml index dc727e4d..8c2d62c3 100644 --- a/deny.toml +++ b/deny.toml @@ -9,6 +9,7 @@ allow = [ "BSD-3-Clause", "Unicode-3.0", "bzip2-1.0.6", + "CC0-1.0", ] [bans]