mirror of https://github.com/chipsenkbeil/distant
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
8.3 KiB
Rust
265 lines
8.3 KiB
Rust
use std::io::{self, Read};
|
|
|
|
use flate2::bufread::{
|
|
DeflateDecoder, DeflateEncoder, GzDecoder, GzEncoder, ZlibDecoder, ZlibEncoder,
|
|
};
|
|
use flate2::Compression;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use super::{Codec, Frame};
|
|
|
|
/// Represents the level of compression to apply to data
|
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
pub enum CompressionLevel {
|
|
/// Use no compression (can potentially inflate data)
|
|
Zero = 0,
|
|
|
|
/// Optimize for the speed of encoding
|
|
One = 1,
|
|
|
|
Two = 2,
|
|
Three = 3,
|
|
Four = 4,
|
|
Five = 5,
|
|
Six = 6,
|
|
Seven = 7,
|
|
Eight = 8,
|
|
|
|
/// Optimize for the size of data being encoded
|
|
Nine = 9,
|
|
}
|
|
|
|
impl CompressionLevel {
|
|
/// Applies best compression to reduce size (slowest)
|
|
pub const BEST: Self = Self::Nine;
|
|
/// Applies fastest compression
|
|
pub const FAST: Self = Self::One;
|
|
/// Applies no compression
|
|
pub const NONE: Self = Self::Zero;
|
|
}
|
|
|
|
impl Default for CompressionLevel {
|
|
/// Standard compression level used in zlib library is 6, which is also used here
|
|
fn default() -> Self {
|
|
Self::Six
|
|
}
|
|
}
|
|
|
|
/// Represents the type of compression for a [`CompressionCodec`]
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum CompressionType {
|
|
Deflate,
|
|
Gzip,
|
|
Zlib,
|
|
|
|
/// Indicates an unknown compression type for use in handshakes
|
|
#[serde(other)]
|
|
Unknown,
|
|
}
|
|
|
|
impl CompressionType {
|
|
/// Returns a list of all variants of the type *except* unknown.
|
|
pub const fn known_variants() -> &'static [CompressionType] {
|
|
&[
|
|
CompressionType::Deflate,
|
|
CompressionType::Gzip,
|
|
CompressionType::Zlib,
|
|
]
|
|
}
|
|
|
|
/// Returns true if type is unknown
|
|
pub fn is_unknown(&self) -> bool {
|
|
matches!(self, Self::Unknown)
|
|
}
|
|
|
|
/// Creates a new [`CompressionCodec`] for this type, failing if this type is unknown
|
|
pub fn new_codec(&self, level: CompressionLevel) -> io::Result<CompressionCodec> {
|
|
CompressionCodec::from_type_and_level(*self, level)
|
|
}
|
|
}
|
|
|
|
/// Represents a codec that applies compression during encoding and decompression during decoding
|
|
/// of a frame's item
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
pub enum CompressionCodec {
|
|
/// Apply DEFLATE compression/decompression using compression `level`
|
|
Deflate { level: CompressionLevel },
|
|
|
|
/// Apply gzip compression/decompression using compression `level`
|
|
Gzip { level: CompressionLevel },
|
|
|
|
/// Apply zlib compression/decompression using compression `level`
|
|
Zlib { level: CompressionLevel },
|
|
}
|
|
|
|
impl CompressionCodec {
|
|
/// Makes a new [`CompressionCodec`] based on the [`CompressionType`] and [`CompressionLevel`],
|
|
/// returning error if the type is unknown
|
|
pub fn from_type_and_level(
|
|
ty: CompressionType,
|
|
level: CompressionLevel,
|
|
) -> io::Result<CompressionCodec> {
|
|
match ty {
|
|
CompressionType::Deflate => Ok(Self::Deflate { level }),
|
|
CompressionType::Gzip => Ok(Self::Gzip { level }),
|
|
CompressionType::Zlib => Ok(Self::Zlib { level }),
|
|
CompressionType::Unknown => Err(io::Error::new(
|
|
io::ErrorKind::InvalidInput,
|
|
"Unknown compression type",
|
|
)),
|
|
}
|
|
}
|
|
|
|
/// Create a new deflate compression codec with the specified `level`
|
|
pub fn deflate(level: impl Into<CompressionLevel>) -> Self {
|
|
Self::Deflate {
|
|
level: level.into(),
|
|
}
|
|
}
|
|
|
|
/// Create a new gzip compression codec with the specified `level`
|
|
pub fn gzip(level: impl Into<CompressionLevel>) -> Self {
|
|
Self::Gzip {
|
|
level: level.into(),
|
|
}
|
|
}
|
|
|
|
/// Create a new zlib compression codec with the specified `level`
|
|
pub fn zlib(level: impl Into<CompressionLevel>) -> Self {
|
|
Self::Zlib {
|
|
level: level.into(),
|
|
}
|
|
}
|
|
|
|
/// Returns the compression level associated with the codec
|
|
pub fn level(&self) -> CompressionLevel {
|
|
match self {
|
|
Self::Deflate { level } => *level,
|
|
Self::Gzip { level } => *level,
|
|
Self::Zlib { level } => *level,
|
|
}
|
|
}
|
|
|
|
/// Returns the compression type associated with the codec
|
|
pub fn ty(&self) -> CompressionType {
|
|
match self {
|
|
Self::Deflate { .. } => CompressionType::Deflate,
|
|
Self::Gzip { .. } => CompressionType::Gzip,
|
|
Self::Zlib { .. } => CompressionType::Zlib,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for CompressionCodec {
|
|
fn encode<'a>(&mut self, frame: Frame<'a>) -> io::Result<Frame<'a>> {
|
|
let item = frame.as_item();
|
|
|
|
let mut buf = Vec::new();
|
|
match *self {
|
|
Self::Deflate { level } => {
|
|
DeflateEncoder::new(item, Compression::new(level as u32)).read_to_end(&mut buf)?
|
|
}
|
|
Self::Gzip { level } => {
|
|
GzEncoder::new(item, Compression::new(level as u32)).read_to_end(&mut buf)?
|
|
}
|
|
Self::Zlib { level } => {
|
|
ZlibEncoder::new(item, Compression::new(level as u32)).read_to_end(&mut buf)?
|
|
}
|
|
};
|
|
|
|
Ok(Frame::from(buf))
|
|
}
|
|
|
|
fn decode<'a>(&mut self, frame: Frame<'a>) -> io::Result<Frame<'a>> {
|
|
let item = frame.as_item();
|
|
|
|
let mut buf = Vec::new();
|
|
match *self {
|
|
Self::Deflate { .. } => DeflateDecoder::new(item).read_to_end(&mut buf)?,
|
|
Self::Gzip { .. } => GzDecoder::new(item).read_to_end(&mut buf)?,
|
|
Self::Zlib { .. } => ZlibDecoder::new(item).read_to_end(&mut buf)?,
|
|
};
|
|
|
|
Ok(Frame::from(buf))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use test_log::test;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn encode_should_apply_appropriate_compression_algorithm() {
|
|
// Encode using DEFLATE and verify that the compression was as expected by decompressing
|
|
let mut codec = CompressionCodec::deflate(CompressionLevel::BEST);
|
|
let frame = codec.encode(Frame::new(b"some bytes")).unwrap();
|
|
|
|
let mut item = Vec::new();
|
|
DeflateDecoder::new(frame.as_item())
|
|
.read_to_end(&mut item)
|
|
.unwrap();
|
|
assert_eq!(item, b"some bytes");
|
|
|
|
// Encode using gzip and verify that the compression was as expected by decompressing
|
|
let mut codec = CompressionCodec::gzip(CompressionLevel::BEST);
|
|
let frame = codec.encode(Frame::new(b"some bytes")).unwrap();
|
|
|
|
let mut item = Vec::new();
|
|
GzDecoder::new(frame.as_item())
|
|
.read_to_end(&mut item)
|
|
.unwrap();
|
|
assert_eq!(item, b"some bytes");
|
|
|
|
// Encode using zlib and verify that the compression was as expected by decompressing
|
|
let mut codec = CompressionCodec::zlib(CompressionLevel::BEST);
|
|
let frame = codec.encode(Frame::new(b"some bytes")).unwrap();
|
|
|
|
let mut item = Vec::new();
|
|
ZlibDecoder::new(frame.as_item())
|
|
.read_to_end(&mut item)
|
|
.unwrap();
|
|
assert_eq!(item, b"some bytes");
|
|
}
|
|
|
|
#[test]
|
|
fn decode_should_apply_appropriate_decompression_algorithm() {
|
|
// Decode using DEFLATE
|
|
let frame = {
|
|
let mut item = Vec::new();
|
|
DeflateEncoder::new(b"some bytes".as_slice(), Compression::best())
|
|
.read_to_end(&mut item)
|
|
.unwrap();
|
|
Frame::from(item)
|
|
};
|
|
let mut codec = CompressionCodec::deflate(CompressionLevel::BEST);
|
|
let frame = codec.decode(frame).unwrap();
|
|
assert_eq!(frame, b"some bytes");
|
|
|
|
// Decode using gzip
|
|
let frame = {
|
|
let mut item = Vec::new();
|
|
GzEncoder::new(b"some bytes".as_slice(), Compression::best())
|
|
.read_to_end(&mut item)
|
|
.unwrap();
|
|
Frame::from(item)
|
|
};
|
|
let mut codec = CompressionCodec::gzip(CompressionLevel::BEST);
|
|
let frame = codec.decode(frame).unwrap();
|
|
assert_eq!(frame, b"some bytes");
|
|
|
|
// Decode using zlib
|
|
let frame = {
|
|
let mut item = Vec::new();
|
|
ZlibEncoder::new(b"some bytes".as_slice(), Compression::best())
|
|
.read_to_end(&mut item)
|
|
.unwrap();
|
|
Frame::from(item)
|
|
};
|
|
let mut codec = CompressionCodec::zlib(CompressionLevel::BEST);
|
|
let frame = codec.decode(frame).unwrap();
|
|
assert_eq!(frame, b"some bytes");
|
|
}
|
|
}
|