use crate::error::{Error, Result};
use std::io::Read;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionType {
None,
Gzip,
Deflate,
#[cfg(feature = "brotli")]
Brotli,
}
impl CompressionType {
pub fn from_encoding(encoding: &str) -> Self {
match encoding.to_lowercase().as_str() {
"gzip" | "x-gzip" => CompressionType::Gzip,
"deflate" => CompressionType::Deflate,
#[cfg(feature = "brotli")]
"br" => CompressionType::Brotli,
_ => CompressionType::None,
}
}
pub fn as_encoding(self) -> &'static str {
match self {
CompressionType::None => "identity",
CompressionType::Gzip => "gzip",
CompressionType::Deflate => "deflate",
#[cfg(feature = "brotli")]
CompressionType::Brotli => "br",
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum TransformError {
#[error("Transformation error: {0}")]
Transform(String),
#[error("Invalid compressed data: {0}")]
InvalidData(String),
#[error("Unsupported compression type: {0:?}")]
UnsupportedType(CompressionType),
}
pub trait StreamTransform {
fn transform(&mut self, input: &[u8]) -> Result<Vec<u8>>;
fn finalize(&mut self) -> Result<Vec<u8>> {
Ok(Vec::new())
}
fn reset(&mut self) -> Result<()>;
}
pub struct GzipDecoder {
decoder: Option<flate2::read::GzDecoder<std::io::Cursor<Vec<u8>>>>,
buffer: Vec<u8>,
}
impl Default for GzipDecoder {
fn default() -> Self {
Self::new()
}
}
impl GzipDecoder {
pub fn new() -> Self {
Self {
decoder: None,
buffer: Vec::new(),
}
}
}
impl StreamTransform for GzipDecoder {
fn transform(&mut self, input: &[u8]) -> Result<Vec<u8>> {
self.buffer.extend_from_slice(input);
if self.decoder.is_none() {
self.decoder = Some(flate2::read::GzDecoder::new(std::io::Cursor::new(
self.buffer.clone(),
)));
}
let mut output = Vec::new();
if let Some(ref mut decoder) = self.decoder {
decoder
.read_to_end(&mut output)
.map_err(|e| Error::Transform(TransformError::InvalidData(e.to_string())))?;
}
Ok(output)
}
fn finalize(&mut self) -> Result<Vec<u8>> {
if self.decoder.is_none() && !self.buffer.is_empty() {
self.transform(&[])
} else {
Ok(Vec::new())
}
}
fn reset(&mut self) -> Result<()> {
self.decoder = None;
self.buffer.clear();
Ok(())
}
}
pub struct DeflateDecoder {
decoder: Option<flate2::read::DeflateDecoder<std::io::Cursor<Vec<u8>>>>,
buffer: Vec<u8>,
}
impl Default for DeflateDecoder {
fn default() -> Self {
Self::new()
}
}
impl DeflateDecoder {
pub fn new() -> Self {
Self {
decoder: None,
buffer: Vec::new(),
}
}
}
impl StreamTransform for DeflateDecoder {
fn transform(&mut self, input: &[u8]) -> Result<Vec<u8>> {
self.buffer.extend_from_slice(input);
if self.decoder.is_none() {
self.decoder = Some(flate2::read::DeflateDecoder::new(std::io::Cursor::new(
self.buffer.clone(),
)));
}
let mut output = Vec::new();
if let Some(ref mut decoder) = self.decoder {
decoder
.read_to_end(&mut output)
.map_err(|e| Error::Transform(TransformError::InvalidData(e.to_string())))?;
}
Ok(output)
}
fn finalize(&mut self) -> Result<Vec<u8>> {
if self.decoder.is_none() && !self.buffer.is_empty() {
self.transform(&[])
} else {
Ok(Vec::new())
}
}
fn reset(&mut self) -> Result<()> {
self.decoder = None;
self.buffer.clear();
Ok(())
}
}
#[cfg(feature = "brotli")]
pub struct BrotliDecoder {
buffer: Vec<u8>,
}
#[cfg(feature = "brotli")]
impl Default for BrotliDecoder {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "brotli")]
impl BrotliDecoder {
pub fn new() -> Self {
Self { buffer: Vec::new() }
}
}
#[cfg(feature = "brotli")]
impl StreamTransform for BrotliDecoder {
fn transform(&mut self, input: &[u8]) -> Result<Vec<u8>> {
self.buffer.extend_from_slice(input);
let mut decoder = brotli::Decompressor::new(std::io::Cursor::new(&self.buffer), 4096);
let mut output = Vec::new();
decoder
.read_to_end(&mut output)
.map_err(|e| Error::Transform(TransformError::InvalidData(e.to_string())))?;
Ok(output)
}
fn finalize(&mut self) -> Result<Vec<u8>> {
if self.buffer.is_empty() {
Ok(Vec::new())
} else {
self.transform(&[])
}
}
fn reset(&mut self) -> Result<()> {
self.buffer.clear();
Ok(())
}
}
pub fn create_decoder(compression_type: CompressionType) -> Result<Box<dyn StreamTransform>> {
match compression_type {
CompressionType::None => Err(Error::Transform(TransformError::UnsupportedType(
CompressionType::None,
))),
CompressionType::Gzip => Ok(Box::new(GzipDecoder::new())),
CompressionType::Deflate => Ok(Box::new(DeflateDecoder::new())),
#[cfg(feature = "brotli")]
CompressionType::Brotli => Ok(Box::new(BrotliDecoder::new())),
}
}
pub fn decompress(data: &[u8], compression_type: CompressionType) -> Result<Vec<u8>> {
let mut decoder = create_decoder(compression_type)?;
let result = decoder.transform(data)?;
let final_data = decoder.finalize()?;
let mut output = result;
output.extend_from_slice(&final_data);
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
use flate2::write::DeflateEncoder;
use flate2::{Compression as FlateCompression, write::GzEncoder};
use std::io::Write;
fn create_gzip_data(data: &[u8]) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), FlateCompression::default());
encoder.write_all(data).unwrap();
encoder.finish().unwrap()
}
fn create_deflate_data(data: &[u8]) -> Vec<u8> {
let mut encoder = DeflateEncoder::new(Vec::new(), FlateCompression::default());
encoder.write_all(data).unwrap();
encoder.finish().unwrap()
}
#[test]
fn test_compression_type_detection() {
assert_eq!(
CompressionType::from_encoding("gzip"),
CompressionType::Gzip
);
assert_eq!(
CompressionType::from_encoding("GZIP"),
CompressionType::Gzip
);
assert_eq!(
CompressionType::from_encoding("x-gzip"),
CompressionType::Gzip
);
assert_eq!(
CompressionType::from_encoding("deflate"),
CompressionType::Deflate
);
assert_eq!(
CompressionType::from_encoding("unknown"),
CompressionType::None
);
assert_eq!(CompressionType::from_encoding(""), CompressionType::None);
}
#[test]
fn test_gzip_decompression() {
let original = b"Hello, World! This is a test string for gzip compression.";
let compressed = create_gzip_data(original);
let mut decoder = GzipDecoder::new();
let decompressed = decoder.transform(&compressed).unwrap();
assert_eq!(decompressed, original);
}
#[test]
fn test_deflate_decompression() {
let original = b"Hello, World! This is a test string for deflate compression.";
let compressed = create_deflate_data(original);
let mut decoder = DeflateDecoder::new();
let decompressed = decoder.transform(&compressed).unwrap();
assert_eq!(decompressed, original);
}
#[test]
fn test_convenience_function() {
let original = b"Test data for convenience function.";
let gzip_data = create_gzip_data(original);
let deflate_data = create_deflate_data(original);
let decompressed = decompress(&gzip_data, CompressionType::Gzip).unwrap();
assert_eq!(decompressed, original);
let decompressed = decompress(&deflate_data, CompressionType::Deflate).unwrap();
assert_eq!(decompressed, original);
}
#[test]
fn test_decoder_factory() {
let gzip_decoder = create_decoder(CompressionType::Gzip);
assert!(gzip_decoder.is_ok());
let deflate_decoder = create_decoder(CompressionType::Deflate);
assert!(deflate_decoder.is_ok());
let none_decoder = create_decoder(CompressionType::None);
assert!(none_decoder.is_err());
}
#[test]
fn test_invalid_gzip_data() {
let invalid_data = b"This is not valid gzip data";
let mut decoder = GzipDecoder::new();
let result = decoder.transform(invalid_data);
assert!(result.is_err());
}
#[test]
fn test_invalid_deflate_data() {
let invalid_data = b"This is not valid deflate data";
let mut decoder = DeflateDecoder::new();
let result = decoder.transform(invalid_data);
assert!(result.is_err());
}
#[test]
fn test_decoder_reset() {
let original = b"Test data for reset.";
let compressed = create_gzip_data(original);
let mut decoder = GzipDecoder::new();
let result1 = decoder.transform(&compressed).unwrap();
assert_eq!(result1, original);
decoder.reset().unwrap();
let result2 = decoder.transform(&compressed).unwrap();
assert_eq!(result2, original);
}
#[test]
fn test_empty_data() {
let mut decoder = GzipDecoder::new();
let result = decoder.transform(&[]);
assert!(result.is_err() || result.unwrap().is_empty());
}
#[test]
fn test_large_data() {
let original: Vec<u8> = (0..10000).map(|i| (i % 256) as u8).collect();
let compressed = create_gzip_data(&original);
let mut decoder = GzipDecoder::new();
let decompressed = decoder.transform(&compressed).unwrap();
assert_eq!(decompressed, original);
}
}