1#![cfg_attr(
26 feature = "experimental",
27 doc = r"
28# Example
29
30```no_run
31use parquet::{basic::Compression, compression::{create_codec, CodecOptionsBuilder}};
32
33let codec_options = CodecOptionsBuilder::default()
34 .set_backward_compatible_lz4(false)
35 .build();
36let mut codec = match create_codec(Compression::SNAPPY, &codec_options) {
37 Ok(Some(codec)) => codec,
38 _ => panic!(),
39};
40
41let data = vec![b'p', b'a', b'r', b'q', b'u', b'e', b't'];
42let mut compressed = vec![];
43codec.compress(&data[..], &mut compressed).unwrap();
44
45let mut output = vec![];
46codec.decompress(&compressed[..], &mut output, None).unwrap();
47
48assert_eq!(output, data);
49```
50"
51)]
52use crate::basic::Compression as CodecType;
53use crate::errors::{ParquetError, Result};
54
55pub trait Codec: Send {
57 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()>;
63
64 fn decompress(
72 &mut self,
73 input_buf: &[u8],
74 output_buf: &mut Vec<u8>,
75 uncompress_size: Option<usize>,
76 ) -> Result<usize>;
77}
78
79#[derive(Debug, PartialEq, Eq)]
81pub struct CodecOptions {
82 backward_compatible_lz4: bool,
84}
85
86impl Default for CodecOptions {
87 fn default() -> Self {
88 CodecOptionsBuilder::default().build()
89 }
90}
91
92pub struct CodecOptionsBuilder {
93 backward_compatible_lz4: bool,
95}
96
97impl Default for CodecOptionsBuilder {
98 fn default() -> Self {
99 Self {
100 backward_compatible_lz4: true,
101 }
102 }
103}
104
105impl CodecOptionsBuilder {
106 pub fn set_backward_compatible_lz4(mut self, value: bool) -> CodecOptionsBuilder {
115 self.backward_compatible_lz4 = value;
116 self
117 }
118
119 pub fn build(self) -> CodecOptions {
120 CodecOptions {
121 backward_compatible_lz4: self.backward_compatible_lz4,
122 }
123 }
124}
125
126pub(crate) trait CompressionLevel<T: std::fmt::Display + std::cmp::PartialOrd> {
128 const MINIMUM_LEVEL: T;
129 const MAXIMUM_LEVEL: T;
130
131 fn is_valid_level(level: T) -> Result<()> {
133 let compression_range = Self::MINIMUM_LEVEL..=Self::MAXIMUM_LEVEL;
134 if compression_range.contains(&level) {
135 Ok(())
136 } else {
137 Err(ParquetError::General(format!(
138 "valid compression range {}..={} exceeded.",
139 compression_range.start(),
140 compression_range.end()
141 )))
142 }
143 }
144}
145
146pub fn create_codec(codec: CodecType, _options: &CodecOptions) -> Result<Option<Box<dyn Codec>>> {
150 #[allow(unreachable_code, unused_variables)]
151 match codec {
152 CodecType::BROTLI(level) => {
153 #[cfg(any(feature = "brotli", test))]
154 return Ok(Some(Box::new(BrotliCodec::new(level))));
155 Err(ParquetError::General(
156 "Disabled feature at compile time: brotli".into(),
157 ))
158 }
159 CodecType::GZIP(level) => {
160 #[cfg(any(feature = "flate2", test))]
161 return Ok(Some(Box::new(GZipCodec::new(level))));
162 Err(ParquetError::General(
163 "Disabled feature at compile time: flate2".into(),
164 ))
165 }
166 CodecType::SNAPPY => {
167 #[cfg(any(feature = "snap", test))]
168 return Ok(Some(Box::new(SnappyCodec::new())));
169 Err(ParquetError::General(
170 "Disabled feature at compile time: snap".into(),
171 ))
172 }
173 CodecType::LZ4 => {
174 #[cfg(any(feature = "lz4", test))]
175 return Ok(Some(Box::new(LZ4HadoopCodec::new(
176 _options.backward_compatible_lz4,
177 ))));
178 Err(ParquetError::General(
179 "Disabled feature at compile time: lz4".into(),
180 ))
181 }
182 CodecType::ZSTD(level) => {
183 #[cfg(any(feature = "zstd", test))]
184 return Ok(Some(Box::new(ZSTDCodec::new(level))));
185 Err(ParquetError::General(
186 "Disabled feature at compile time: zstd".into(),
187 ))
188 }
189 CodecType::LZ4_RAW => {
190 #[cfg(any(feature = "lz4", test))]
191 return Ok(Some(Box::new(LZ4RawCodec::new())));
192 Err(ParquetError::General(
193 "Disabled feature at compile time: lz4".into(),
194 ))
195 }
196 CodecType::UNCOMPRESSED => Ok(None),
197 _ => Err(nyi_err!("The codec type {} is not supported yet", codec)),
198 }
199}
200
201#[cfg(any(feature = "snap", test))]
202mod snappy_codec {
203 use snap::raw::{Decoder, Encoder, decompress_len, max_compress_len};
204
205 use crate::compression::Codec;
206 use crate::errors::Result;
207
208 pub struct SnappyCodec {
210 decoder: Decoder,
211 encoder: Encoder,
212 }
213
214 impl SnappyCodec {
215 pub(crate) fn new() -> Self {
217 Self {
218 decoder: Decoder::new(),
219 encoder: Encoder::new(),
220 }
221 }
222 }
223
224 impl Codec for SnappyCodec {
225 fn decompress(
226 &mut self,
227 input_buf: &[u8],
228 output_buf: &mut Vec<u8>,
229 uncompress_size: Option<usize>,
230 ) -> Result<usize> {
231 let len = match uncompress_size {
232 Some(size) => size,
233 None => decompress_len(input_buf)?,
234 };
235 let offset = output_buf.len();
236 output_buf.resize(offset + len, 0);
237 self.decoder
238 .decompress(input_buf, &mut output_buf[offset..])
239 .map_err(|e| e.into())
240 }
241
242 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
243 let output_buf_len = output_buf.len();
244 let required_len = max_compress_len(input_buf.len());
245 output_buf.resize(output_buf_len + required_len, 0);
246 let n = self
247 .encoder
248 .compress(input_buf, &mut output_buf[output_buf_len..])?;
249 output_buf.truncate(output_buf_len + n);
250 Ok(())
251 }
252 }
253}
254#[cfg(any(feature = "snap", test))]
255pub use snappy_codec::*;
256
257#[cfg(any(feature = "flate2", test))]
258mod gzip_codec {
259
260 use std::io::{Read, Write};
261
262 use flate2::{Compression, read, write};
263
264 use crate::compression::Codec;
265 use crate::errors::Result;
266
267 use super::GzipLevel;
268
269 pub struct GZipCodec {
271 level: GzipLevel,
272 }
273
274 impl GZipCodec {
275 pub(crate) fn new(level: GzipLevel) -> Self {
277 Self { level }
278 }
279 }
280
281 impl Codec for GZipCodec {
282 fn decompress(
283 &mut self,
284 input_buf: &[u8],
285 output_buf: &mut Vec<u8>,
286 _uncompress_size: Option<usize>,
287 ) -> Result<usize> {
288 let mut decoder = read::MultiGzDecoder::new(input_buf);
289 decoder.read_to_end(output_buf).map_err(|e| e.into())
290 }
291
292 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
293 let mut encoder = write::GzEncoder::new(output_buf, Compression::new(self.level.0));
294 encoder.write_all(input_buf)?;
295 encoder.try_finish().map_err(|e| e.into())
296 }
297 }
298}
299#[cfg(any(feature = "flate2", test))]
300pub use gzip_codec::*;
301
302#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
333pub struct GzipLevel(u32);
334
335impl Default for GzipLevel {
336 fn default() -> Self {
337 Self(6)
340 }
341}
342
343impl CompressionLevel<u32> for GzipLevel {
344 const MINIMUM_LEVEL: u32 = 0;
345 const MAXIMUM_LEVEL: u32 = 9;
346}
347
348impl GzipLevel {
349 pub fn try_new(level: u32) -> Result<Self> {
353 Self::is_valid_level(level).map(|()| Self(level))
354 }
355
356 pub fn compression_level(&self) -> u32 {
358 self.0
359 }
360}
361
362#[cfg(any(feature = "brotli", test))]
363mod brotli_codec {
364
365 use std::io::{Read, Write};
366
367 use crate::compression::Codec;
368 use crate::errors::Result;
369
370 use super::BrotliLevel;
371
372 const BROTLI_DEFAULT_BUFFER_SIZE: usize = 4096;
373 const BROTLI_DEFAULT_LG_WINDOW_SIZE: u32 = 22; pub struct BrotliCodec {
377 level: BrotliLevel,
378 }
379
380 impl BrotliCodec {
381 pub(crate) fn new(level: BrotliLevel) -> Self {
383 Self { level }
384 }
385 }
386
387 impl Codec for BrotliCodec {
388 fn decompress(
389 &mut self,
390 input_buf: &[u8],
391 output_buf: &mut Vec<u8>,
392 uncompress_size: Option<usize>,
393 ) -> Result<usize> {
394 let buffer_size = uncompress_size.unwrap_or(BROTLI_DEFAULT_BUFFER_SIZE);
395 brotli::Decompressor::new(input_buf, buffer_size)
396 .read_to_end(output_buf)
397 .map_err(|e| e.into())
398 }
399
400 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
401 let mut encoder = brotli::CompressorWriter::new(
402 output_buf,
403 BROTLI_DEFAULT_BUFFER_SIZE,
404 self.level.0,
405 BROTLI_DEFAULT_LG_WINDOW_SIZE,
406 );
407 encoder.write_all(input_buf)?;
408 encoder.flush().map_err(|e| e.into())
409 }
410 }
411}
412#[cfg(any(feature = "brotli", test))]
413pub use brotli_codec::*;
414
415#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
417pub struct BrotliLevel(u32);
418
419impl Default for BrotliLevel {
420 fn default() -> Self {
421 Self(1)
422 }
423}
424
425impl CompressionLevel<u32> for BrotliLevel {
426 const MINIMUM_LEVEL: u32 = 0;
427 const MAXIMUM_LEVEL: u32 = 11;
428}
429
430impl BrotliLevel {
431 pub fn try_new(level: u32) -> Result<Self> {
435 Self::is_valid_level(level).map(|()| Self(level))
436 }
437
438 pub fn compression_level(&self) -> u32 {
440 self.0
441 }
442}
443
444#[cfg(any(feature = "lz4", test))]
445mod lz4_codec {
446 use std::io::{Read, Write};
447
448 use crate::compression::Codec;
449 use crate::errors::{ParquetError, Result};
450
451 const LZ4_BUFFER_SIZE: usize = 4096;
452
453 pub struct LZ4Codec {}
455
456 impl LZ4Codec {
457 pub(crate) fn new() -> Self {
459 Self {}
460 }
461 }
462
463 impl Codec for LZ4Codec {
464 fn decompress(
465 &mut self,
466 input_buf: &[u8],
467 output_buf: &mut Vec<u8>,
468 _uncompress_size: Option<usize>,
469 ) -> Result<usize> {
470 let mut decoder = lz4_flex::frame::FrameDecoder::new(input_buf);
471 let mut buffer: [u8; LZ4_BUFFER_SIZE] = [0; LZ4_BUFFER_SIZE];
472 let mut total_len = 0;
473 loop {
474 let len = decoder.read(&mut buffer)?;
475 if len == 0 {
476 break;
477 }
478 total_len += len;
479 output_buf.write_all(&buffer[0..len])?;
480 }
481 Ok(total_len)
482 }
483
484 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
485 let mut encoder = lz4_flex::frame::FrameEncoder::new(output_buf);
486 let mut from = 0;
487 loop {
488 let to = std::cmp::min(from + LZ4_BUFFER_SIZE, input_buf.len());
489 encoder.write_all(&input_buf[from..to])?;
490 from += LZ4_BUFFER_SIZE;
491 if from >= input_buf.len() {
492 break;
493 }
494 }
495 match encoder.finish() {
496 Ok(_) => Ok(()),
497 Err(e) => Err(ParquetError::External(Box::new(e))),
498 }
499 }
500 }
501}
502
503#[cfg(all(feature = "experimental", any(feature = "lz4", test)))]
504pub use lz4_codec::*;
505
506#[cfg(any(feature = "zstd", test))]
507mod zstd_codec {
508 use crate::compression::{Codec, ZstdLevel};
509 use crate::errors::Result;
510 use std::io::Cursor;
511
512 pub struct ZSTDCodec {
517 compressor: zstd::bulk::Compressor<'static>,
518 decompressor: zstd::bulk::Decompressor<'static>,
519 }
520
521 impl ZSTDCodec {
522 pub(crate) fn new(level: ZstdLevel) -> Self {
524 Self {
525 compressor: zstd::bulk::Compressor::new(level.compression_level())
526 .expect("valid zstd compression level"),
527 decompressor: zstd::bulk::Decompressor::new()
528 .expect("can create zstd decompressor"),
529 }
530 }
531 }
532
533 impl Codec for ZSTDCodec {
534 fn decompress(
535 &mut self,
536 input_buf: &[u8],
537 output_buf: &mut Vec<u8>,
538 uncompress_size: Option<usize>,
539 ) -> Result<usize> {
540 let offset = output_buf.len();
541 let len = uncompress_size
542 .or_else(|| {
543 zstd::zstd_safe::get_frame_content_size(input_buf)
545 .ok()
546 .flatten()
547 .map(|size| size as usize)
548 })
549 .unwrap_or(input_buf.len().saturating_mul(4));
550 output_buf.reserve(len);
551
552 let mut cursor = Cursor::new(output_buf);
553 cursor.set_position(offset as u64);
554 let len = self
555 .decompressor
556 .decompress_to_buffer(input_buf, &mut cursor)?;
557 Ok(len)
558 }
559
560 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
561 let offset = output_buf.len();
562 let len = zstd::zstd_safe::compress_bound(input_buf.len());
563 output_buf.reserve(len);
564
565 let mut cursor = Cursor::new(output_buf);
566 cursor.set_position(offset as u64);
567 let _written = self.compressor.compress_to_buffer(input_buf, &mut cursor)?;
568 Ok(())
569 }
570 }
571}
572#[cfg(any(feature = "zstd", test))]
573pub use zstd_codec::*;
574
575#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
577pub struct ZstdLevel(i32);
578
579impl CompressionLevel<i32> for ZstdLevel {
580 const MINIMUM_LEVEL: i32 = -131072;
583 const MAXIMUM_LEVEL: i32 = 22;
584}
585
586impl ZstdLevel {
587 pub fn try_new(level: i32) -> Result<Self> {
591 Self::is_valid_level(level).map(|()| Self(level))
592 }
593
594 pub fn compression_level(&self) -> i32 {
596 self.0
597 }
598}
599
600impl Default for ZstdLevel {
601 fn default() -> Self {
602 Self(1)
603 }
604}
605
606#[cfg(any(feature = "lz4", test))]
607mod lz4_raw_codec {
608 use crate::compression::Codec;
609 use crate::errors::ParquetError;
610 use crate::errors::Result;
611
612 pub struct LZ4RawCodec {}
614
615 impl LZ4RawCodec {
616 pub(crate) fn new() -> Self {
618 Self {}
619 }
620 }
621
622 impl Codec for LZ4RawCodec {
623 fn decompress(
624 &mut self,
625 input_buf: &[u8],
626 output_buf: &mut Vec<u8>,
627 uncompress_size: Option<usize>,
628 ) -> Result<usize> {
629 let offset = output_buf.len();
630 let required_len = match uncompress_size {
631 Some(uncompress_size) => uncompress_size,
632 None => {
633 return Err(ParquetError::General(
634 "LZ4RawCodec unsupported without uncompress_size".into(),
635 ));
636 }
637 };
638 output_buf.resize(offset + required_len, 0);
639 match lz4_flex::block::decompress_into(input_buf, &mut output_buf[offset..]) {
640 Ok(n) => {
641 if n != required_len {
642 return Err(ParquetError::General(
643 "LZ4RawCodec uncompress_size is not the expected one".into(),
644 ));
645 }
646 Ok(n)
647 }
648 Err(e) => Err(ParquetError::External(Box::new(e))),
649 }
650 }
651
652 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
653 let offset = output_buf.len();
654 let required_len = lz4_flex::block::get_maximum_output_size(input_buf.len());
655 output_buf.resize(offset + required_len, 0);
656 match lz4_flex::block::compress_into(input_buf, &mut output_buf[offset..]) {
657 Ok(n) => {
658 output_buf.truncate(offset + n);
659 Ok(())
660 }
661 Err(e) => Err(ParquetError::External(Box::new(e))),
662 }
663 }
664 }
665}
666#[cfg(any(feature = "lz4", test))]
667pub use lz4_raw_codec::*;
668
669#[cfg(any(feature = "lz4", test))]
670mod lz4_hadoop_codec {
671 use crate::compression::Codec;
672 use crate::compression::lz4_codec::LZ4Codec;
673 use crate::compression::lz4_raw_codec::LZ4RawCodec;
674 use crate::errors::{ParquetError, Result};
675 use std::io;
676
677 const SIZE_U32: usize = std::mem::size_of::<u32>();
679
680 const PREFIX_LEN: usize = SIZE_U32 * 2;
682
683 pub struct LZ4HadoopCodec {
685 backward_compatible_lz4: bool,
689 }
690
691 impl LZ4HadoopCodec {
692 pub(crate) fn new(backward_compatible_lz4: bool) -> Self {
694 Self {
695 backward_compatible_lz4,
696 }
697 }
698 }
699
700 fn try_decompress_hadoop(input_buf: &[u8], output_buf: &mut [u8]) -> io::Result<usize> {
705 let mut input_len = input_buf.len();
715 let mut input = input_buf;
716 let mut read_bytes = 0;
717 let mut output_len = output_buf.len();
718 let mut output: &mut [u8] = output_buf;
719 while input_len >= PREFIX_LEN {
720 let mut bytes = [0; SIZE_U32];
721 bytes.copy_from_slice(&input[0..4]);
722 let expected_decompressed_size = u32::from_be_bytes(bytes);
723 let mut bytes = [0; SIZE_U32];
724 bytes.copy_from_slice(&input[4..8]);
725 let expected_compressed_size = u32::from_be_bytes(bytes);
726 input = &input[PREFIX_LEN..];
727 input_len -= PREFIX_LEN;
728
729 if input_len < expected_compressed_size as usize {
730 return Err(io::Error::other("Not enough bytes for Hadoop frame"));
731 }
732
733 if output_len < expected_decompressed_size as usize {
734 return Err(io::Error::other(
735 "Not enough bytes to hold advertised output",
736 ));
737 }
738 let decompressed_size =
739 lz4_flex::decompress_into(&input[..expected_compressed_size as usize], output)
740 .map_err(|e| ParquetError::External(Box::new(e)))?;
741 if decompressed_size != expected_decompressed_size as usize {
742 return Err(io::Error::other("Unexpected decompressed size"));
743 }
744 input_len -= expected_compressed_size as usize;
745 output_len -= expected_decompressed_size as usize;
746 read_bytes += expected_decompressed_size as usize;
747 if input_len > expected_compressed_size as usize {
748 input = &input[expected_compressed_size as usize..];
749 output = &mut output[expected_decompressed_size as usize..];
750 } else {
751 break;
752 }
753 }
754 if input_len == 0 {
755 Ok(read_bytes)
756 } else {
757 Err(io::Error::other("Not all input are consumed"))
758 }
759 }
760
761 impl Codec for LZ4HadoopCodec {
762 fn decompress(
763 &mut self,
764 input_buf: &[u8],
765 output_buf: &mut Vec<u8>,
766 uncompress_size: Option<usize>,
767 ) -> Result<usize> {
768 let output_len = output_buf.len();
769 let required_len = match uncompress_size {
770 Some(n) => n,
771 None => {
772 return Err(ParquetError::General(
773 "LZ4HadoopCodec unsupported without uncompress_size".into(),
774 ));
775 }
776 };
777 output_buf.resize(output_len + required_len, 0);
778 match try_decompress_hadoop(input_buf, &mut output_buf[output_len..]) {
779 Ok(n) => {
780 if n != required_len {
781 return Err(ParquetError::General(
782 "LZ4HadoopCodec uncompress_size is not the expected one".into(),
783 ));
784 }
785 Ok(n)
786 }
787 Err(e) if !self.backward_compatible_lz4 => Err(e.into()),
788 Err(_) => {
791 output_buf.truncate(output_len);
793 match LZ4Codec::new().decompress(input_buf, output_buf, uncompress_size) {
794 Ok(n) => Ok(n),
795 Err(_) => {
796 output_buf.truncate(output_len);
798 LZ4RawCodec::new().decompress(input_buf, output_buf, uncompress_size)
799 }
800 }
801 }
802 }
803 }
804
805 fn compress(&mut self, input_buf: &[u8], output_buf: &mut Vec<u8>) -> Result<()> {
806 let offset = output_buf.len();
808 output_buf.resize(offset + PREFIX_LEN, 0);
809
810 LZ4RawCodec::new().compress(input_buf, output_buf)?;
812
813 let output_buf = &mut output_buf[offset..];
816 let compressed_size = output_buf.len() - PREFIX_LEN;
817 let compressed_size = compressed_size as u32;
818 let uncompressed_size = input_buf.len() as u32;
819 output_buf[..SIZE_U32].copy_from_slice(&uncompressed_size.to_be_bytes());
820 output_buf[SIZE_U32..PREFIX_LEN].copy_from_slice(&compressed_size.to_be_bytes());
821
822 Ok(())
823 }
824 }
825}
826#[cfg(any(feature = "lz4", test))]
827pub use lz4_hadoop_codec::*;
828
829#[cfg(test)]
830mod tests {
831 use super::*;
832
833 use crate::util::test_common::rand_gen::random_bytes;
834
835 fn test_roundtrip(c: CodecType, data: &[u8], uncompress_size: Option<usize>) {
836 let codec_options = CodecOptionsBuilder::default()
837 .set_backward_compatible_lz4(false)
838 .build();
839 let mut c1 = create_codec(c, &codec_options).unwrap().unwrap();
840 let mut c2 = create_codec(c, &codec_options).unwrap().unwrap();
841
842 let mut compressed = Vec::new();
844 let mut decompressed = Vec::new();
845 c1.compress(data, &mut compressed)
846 .expect("Error when compressing");
847
848 let decompressed_size = c2
850 .decompress(compressed.as_slice(), &mut decompressed, uncompress_size)
851 .expect("Error when decompressing");
852 assert_eq!(data.len(), decompressed_size);
853 assert_eq!(data, decompressed.as_slice());
854
855 decompressed.clear();
856 compressed.clear();
857
858 c2.compress(data, &mut compressed)
860 .expect("Error when compressing");
861
862 let decompressed_size = c1
864 .decompress(compressed.as_slice(), &mut decompressed, uncompress_size)
865 .expect("Error when decompressing");
866 assert_eq!(data.len(), decompressed_size);
867 assert_eq!(data, decompressed.as_slice());
868
869 decompressed.clear();
870 compressed.clear();
871
872 let prefix = &[0xDE, 0xAD, 0xBE, 0xEF];
874 decompressed.extend_from_slice(prefix);
875 compressed.extend_from_slice(prefix);
876
877 c2.compress(data, &mut compressed)
878 .expect("Error when compressing");
879
880 assert_eq!(&compressed[..4], prefix);
881
882 let decompressed_size = c2
883 .decompress(&compressed[4..], &mut decompressed, uncompress_size)
884 .expect("Error when decompressing");
885
886 assert_eq!(data.len(), decompressed_size);
887 assert_eq!(data, &decompressed[4..]);
888 assert_eq!(&decompressed[..4], prefix);
889 }
890
891 fn test_codec_with_size(c: CodecType) {
892 let sizes = vec![100, 10000, 100000];
893 for size in sizes {
894 let data = random_bytes(size);
895 test_roundtrip(c, &data, Some(data.len()));
896 }
897 }
898
899 fn test_codec_without_size(c: CodecType) {
900 let sizes = vec![100, 10000, 100000];
901 for size in sizes {
902 let data = random_bytes(size);
903 test_roundtrip(c, &data, None);
904 }
905 }
906
907 #[test]
908 fn test_codec_snappy() {
909 test_codec_with_size(CodecType::SNAPPY);
910 test_codec_without_size(CodecType::SNAPPY);
911 }
912
913 #[test]
914 fn test_codec_gzip() {
915 for level in GzipLevel::MINIMUM_LEVEL..=GzipLevel::MAXIMUM_LEVEL {
916 let level = GzipLevel::try_new(level).unwrap();
917 test_codec_with_size(CodecType::GZIP(level));
918 test_codec_without_size(CodecType::GZIP(level));
919 }
920 }
921
922 #[test]
923 fn test_codec_brotli() {
924 for level in BrotliLevel::MINIMUM_LEVEL..=BrotliLevel::MAXIMUM_LEVEL {
925 let level = BrotliLevel::try_new(level).unwrap();
926 test_codec_with_size(CodecType::BROTLI(level));
927 test_codec_without_size(CodecType::BROTLI(level));
928 }
929 }
930
931 #[test]
932 fn test_codec_lz4() {
933 test_codec_with_size(CodecType::LZ4);
934 }
935
936 #[test]
937 fn test_codec_zstd() {
938 for level in [ZstdLevel::MINIMUM_LEVEL]
940 .into_iter()
941 .chain(-100..=ZstdLevel::MAXIMUM_LEVEL)
942 {
943 let level = ZstdLevel::try_new(level).unwrap();
944 test_codec_with_size(CodecType::ZSTD(level));
945 test_codec_without_size(CodecType::ZSTD(level));
946 }
947 }
948
949 #[test]
950 fn test_codec_lz4_raw() {
951 test_codec_with_size(CodecType::LZ4_RAW);
952 }
953}