use crate::error::Result;
use alloc::boxed::Box;
use alloc::vec::Vec;
#[cfg(feature = "compress")]
mod zlib;
#[cfg(feature = "compress")]
pub use zlib::{ZlibCompress, ZlibDecompress, ZlibOpenSshCompress, ZlibOpenSshDecompress};
pub trait Compress: Send {
fn name(&self) -> &'static str;
fn compress(&mut self, input: &[u8]) -> Result<Vec<u8>>;
fn active(&self) -> bool;
fn activate(&mut self);
}
pub trait Decompress: Send {
fn name(&self) -> &'static str;
fn decompress(&mut self, input: &[u8]) -> Result<Vec<u8>>;
fn active(&self) -> bool;
fn activate(&mut self);
}
pub struct NoneCompress;
impl Compress for NoneCompress {
fn name(&self) -> &'static str {
"none"
}
fn compress(&mut self, input: &[u8]) -> Result<Vec<u8>> {
Ok(input.to_vec())
}
fn active(&self) -> bool {
true
}
fn activate(&mut self) {}
}
pub struct NoneDecompress;
impl Decompress for NoneDecompress {
fn name(&self) -> &'static str {
"none"
}
fn decompress(&mut self, input: &[u8]) -> Result<Vec<u8>> {
Ok(input.to_vec())
}
fn active(&self) -> bool {
true
}
fn activate(&mut self) {}
}
pub fn compress_by_name(name: &str) -> Option<Box<dyn Compress>> {
match name {
"none" => Some(Box::new(NoneCompress)),
#[cfg(feature = "compress")]
"zlib" => Some(Box::new(ZlibCompress::new())),
#[cfg(feature = "compress")]
"zlib@openssh.com" => Some(Box::new(ZlibOpenSshCompress::new())),
_ => None,
}
}
pub fn decompress_by_name(name: &str) -> Option<Box<dyn Decompress>> {
match name {
"none" => Some(Box::new(NoneDecompress)),
#[cfg(feature = "compress")]
"zlib" => Some(Box::new(ZlibDecompress::new())),
#[cfg(feature = "compress")]
"zlib@openssh.com" => Some(Box::new(ZlibOpenSshDecompress::new())),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn none_round_trip() {
let mut c = NoneCompress;
let mut d = NoneDecompress;
for payload in [&b""[..], b"x", b"hello world"].iter() {
let on_wire = c.compress(payload).unwrap();
assert_eq!(on_wire.as_slice(), *payload);
let back = d.decompress(&on_wire).unwrap();
assert_eq!(back.as_slice(), *payload);
}
}
#[test]
fn factory_returns_named_instances() {
assert!(compress_by_name("none").is_some());
assert!(decompress_by_name("none").is_some());
assert!(compress_by_name("garbage").is_none());
assert!(decompress_by_name("garbage").is_none());
assert_eq!(compress_by_name("none").unwrap().name(), "none");
assert_eq!(decompress_by_name("none").unwrap().name(), "none");
}
#[cfg(feature = "compress")]
#[test]
fn factory_recognises_zlib_variants() {
assert!(compress_by_name("zlib").is_some());
assert!(compress_by_name("zlib@openssh.com").is_some());
assert!(decompress_by_name("zlib").is_some());
assert!(decompress_by_name("zlib@openssh.com").is_some());
let zlib_dyn = compress_by_name("zlib@openssh.com").unwrap();
assert!(!zlib_dyn.active());
}
}