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