use crate::adler32::Adler32;
use crate::prelude::{deflate, inflate, inflate_to, BlockType, Cache};
use std::convert::TryInto;
const ERROR_ADLER32: &str = "Zlib checksum error";
const ERROR_DEFLATE: &str = "Zlib only supports deflate compression algorithm";
const ERROR_DICT: &str = "Zlib dictionaries is not implemented";
const ERROR_FCHECK: &str = "Fcheck must be multiple of 31";
const ERROR_LENGTH: &str = "Zlib's header is missing";
const HEADER_LEN: usize = 2;
const ADLER_LEN: usize = 4;
const METHOD_DEFLATE: u8 = 8;
pub fn zlib_encode(v_in: &[u8], btype: BlockType, cache: &mut Cache) -> Vec<u8> {
let cmf = 0x78;
let flevel = 2;
let fcheck = 28;
let flg = flevel << 6 | fcheck;
let mut adler32 = Adler32::new();
let mut data = deflate(v_in, btype, cache);
adler32.update(&v_in);
let mut v_out = Vec::with_capacity(data.len() + 2 + 4);
v_out.extend(&[cmf, flg]);
v_out.append(&mut data);
v_out.extend(&adler32.checksum());
v_out
}
pub fn zlib_decode(v_in: &[u8], cache: &mut Cache) -> Result<Vec<u8>, String> {
if v_in.len() < HEADER_LEN + ADLER_LEN {
return Err(ERROR_LENGTH.into());
}
let cmf = v_in[0];
let flg = v_in[1];
if (cmf & 0x0F) != METHOD_DEFLATE {
return Err(ERROR_DEFLATE.into());
}
if ((cmf as u16) << 8 | flg as u16) % 31 != 0 {
return Err(ERROR_FCHECK.into());
}
if (flg & 0b100_000) > 0 {
return Err(ERROR_DICT.into());
}
let mut adler32 = Adler32::new();
let v_out = inflate(&v_in[HEADER_LEN..v_in.len() - ADLER_LEN], cache)?;
adler32.update(&v_out);
let _adler32: [u8; 4] = v_in[v_in.len() - ADLER_LEN..].try_into().unwrap();
if adler32.checksum() != _adler32 {
return Err(ERROR_ADLER32.into());
}
Ok(v_out)
}
pub fn zlib_decode_to(v_in: &[u8], cache: &mut Cache, v_out: &mut [u8]) -> Result<(), String> {
if v_in.len() < HEADER_LEN + ADLER_LEN {
return Err(ERROR_LENGTH.into());
}
let cmf = v_in[0];
let flg = v_in[1];
if (cmf & 0x0F) != METHOD_DEFLATE {
return Err(ERROR_DEFLATE.into());
}
if ((cmf as u16) << 8 | flg as u16) % 31 != 0 {
return Err(ERROR_FCHECK.into());
}
if (flg & 0b100_000) > 0 {
return Err(ERROR_DICT.into());
}
let mut adler32 = Adler32::new();
inflate_to(&v_in[HEADER_LEN..v_in.len() - ADLER_LEN], cache, v_out)?;
adler32.update(v_out);
let _adler32: [u8; 4] = v_in[v_in.len() - ADLER_LEN..].try_into().unwrap();
if adler32.checksum() != _adler32 {
return Err(ERROR_ADLER32.into());
}
Ok(())
}