diff --git a/serde_avro_fast/src/object_container_file_encoding/mod.rs b/serde_avro_fast/src/object_container_file_encoding/mod.rs index 438c740..fb47957 100644 --- a/serde_avro_fast/src/object_container_file_encoding/mod.rs +++ b/serde_avro_fast/src/object_container_file_encoding/mod.rs @@ -25,14 +25,16 @@ pub enum Compression { /// as specified in RFC 1951. Note that this format (unlike the "zlib /// format" in RFC 1950) does not have a checksum. Deflate { - /// Deflate compression level to use + /// Deflate compression level to use (1-9 or + /// [`CompressionLevel::default()`]) level: CompressionLevel, }, #[cfg(feature = "bzip2")] /// The `BZip2` codec uses [BZip2](https://sourceware.org/bzip2/) /// compression library. Bzip2 { - /// BZip2 compression level to use + /// BZip2 compression level to use (1-9 or + /// [`CompressionLevel::default()`]) level: CompressionLevel, }, #[cfg(feature = "snappy")] @@ -44,14 +46,16 @@ pub enum Compression { /// The `Xz` codec uses [Xz utils](https://tukaani.org/xz/) /// compression library. Xz { - /// Xz compression level to use + /// Xz compression level to use (1-9 or + /// [`CompressionLevel::default()`]) level: CompressionLevel, }, #[cfg(feature = "zstandard")] /// The `zstandard` codec uses Facebook’s [Zstandard](https://facebook.github.io/zstd/) /// compression library Zstandard { - /// Zstandard compression level to use + /// Zstandard compression level to use (1-22 or + /// [`CompressionLevel::default()`]) level: CompressionLevel, }, } @@ -68,15 +72,36 @@ impl CompressionLevel { /// Specifies the compression level that will be used for the compression /// algorithms /// + /// # Default value + /// All algorithms have a default compression level configured + /// that may be used by instantiating [`CompressionLevel::default()`]. + /// + /// Use [`CompressionLevel::new`] if you want to specify a specific + /// compression level. + /// + /// # Clipping + /// Depending on the algorithm that will be used, `level` may get clipped + /// into the appropriate range (e.g. set to `9` if higher than `9`). + /// + /// This is done to ensure that the compression level is always within the + /// valid range for the algorithm. + /// + /// See the [`Compression`] enum for the effective ranges for each + /// algorithm. + /// /// # Panics - /// If `level` is lower than `1` or greater than `9` + /// If `level` is lower than `1` /// - /// This is because all algorithms expect compression levels between `1` - /// ("fast compression") and `9` ("take as long as you'd like"). - pub const fn new(level: u8) -> Self { + /// This is because compression level 0 means "actually don't compress" for + /// most algorithms, and in that case one should just use + /// [`Compression::Null`] instead. + pub const fn new(mut level: u8) -> Self { + if level == u8::MAX { + level -= 1; + } match NonZeroU8::new(level) { - Some(n) if n.get() < 10 => Self { repr: n }, - _ => panic!("Compression level must be between 1 and 9"), + Some(n) => Self { repr: n }, + None => panic!("Compression level must be greater than 0"), } } @@ -90,6 +115,18 @@ impl CompressionLevel { } } + #[allow(unused)] + /// may be unused depending on which compression codecs features are enabled + fn clip(mut self, high: u8) -> Self { + if self.repr.get() != u8::MAX { + self.repr = self.repr.min(NonZeroU8::new(high).expect( + "Highest compression level for \ + algorithm should be greater than zero", + )) + } + self + } + #[allow(unused)] /// may be unused depending on which compression codecs features are enabled fn instantiate, F: FnOnce(C) -> T>(self, f: F) -> T { diff --git a/serde_avro_fast/src/object_container_file_encoding/writer/compression.rs b/serde_avro_fast/src/object_container_file_encoding/writer/compression.rs index c076022..9941348 100644 --- a/serde_avro_fast/src/object_container_file_encoding/writer/compression.rs +++ b/serde_avro_fast/src/object_container_file_encoding/writer/compression.rs @@ -14,22 +14,33 @@ impl CompressionCodecState { #[cfg(feature = "deflate")] Compression::Deflate { level } => Kind::Deflate { compress: flate2::Compress::new( - level.instantiate(flate2::Compression::new), + level.clip(9).instantiate(flate2::Compression::new), false, ), }, #[cfg(feature = "bzip2")] - Compression::Bzip2 { level } => Kind::Bzip2 { len: 0, level }, + Compression::Bzip2 { level } => Kind::Bzip2 { + len: 0, + level: level.clip(9), + }, #[cfg(feature = "snappy")] Compression::Snappy => Kind::Snappy { encoder: snap::raw::Encoder::new(), }, #[cfg(feature = "xz")] - Compression::Xz { level } => Kind::Xz { len: 0, level }, + Compression::Xz { level } => Kind::Xz { + len: 0, + level: level.clip(9), + }, #[cfg(feature = "zstandard")] Compression::Zstandard { level } => Kind::Zstandard { compressor: None, - level, + level: level.clip( + (*zstd::compression_level_range().end()) + .max(0) + .try_into() + .unwrap_or(u8::MAX - 1), + ), }, }, }