use alloc::vec::Vec;
use crate::{
Error, ErrorKind, Level,
deflate::{Deflater, Flush, Inflater, MemoryConfig},
};
const TAIL: [u8; 4] = [0x00, 0x00, 0xFF, 0xFF];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct MessageConfig {
window_bits: u8,
context_takeover: bool,
level: Level,
memory: MemoryConfig,
}
impl MessageConfig {
#[must_use]
pub const fn new() -> Self {
Self {
window_bits: 15,
context_takeover: false,
level: Level::FAST,
memory: MemoryConfig::new(),
}
}
#[must_use]
pub const fn with_window_bits(mut self, bits: u8) -> Self {
self.window_bits = bits;
self
}
#[must_use]
pub const fn with_context_takeover(mut self, on: bool) -> Self {
self.context_takeover = on;
self
}
#[must_use]
pub const fn with_level(mut self, level: Level) -> Self {
self.level = level;
self
}
#[must_use]
pub const fn with_memory(mut self, memory: MemoryConfig) -> Self {
self.memory = memory;
self
}
#[must_use]
pub const fn memory(&self) -> MemoryConfig {
self.memory
}
#[must_use]
pub const fn window_bits(&self) -> u8 {
self.window_bits
}
#[must_use]
pub const fn context_takeover(&self) -> bool {
self.context_takeover
}
#[must_use]
pub const fn level(&self) -> Level {
self.level
}
}
impl Default for MessageConfig {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Compressor {
deflater: Deflater,
takeover: bool,
}
impl Compressor {
pub fn new(config: MessageConfig) -> Result<Self, Error> {
let deflater = Deflater::new(config.level)
.with_window_bits(config.window_bits)?
.with_memory(config.memory);
Ok(Self {
deflater,
takeover: config.context_takeover,
})
}
pub fn compress_message(&mut self, message: &[u8], out: &mut Vec<u8>) -> Result<usize, Error> {
let start = out.len();
let result = self.deflater.compress_vec(message, out, Flush::Sync);
if let Err(e) = result {
out.truncate(start);
self.deflater.reset();
return Err(e);
}
if out.len() >= start + TAIL.len() && out[out.len() - TAIL.len()..] == TAIL {
out.truncate(out.len() - TAIL.len());
}
if !self.takeover {
self.deflater.reset();
}
Ok(out.len() - start)
}
pub fn wipe(&mut self) {
self.deflater.wipe();
}
pub fn reset(&mut self) {
self.deflater.reset();
}
#[must_use]
pub fn memory_in_use(&self) -> usize {
self.deflater.memory_in_use()
}
}
#[derive(Debug)]
pub struct Decompressor {
inflater: Inflater,
takeover: bool,
open: Option<OpenMessage>,
}
#[derive(Clone, Copy, Debug)]
struct OpenMessage {
start: usize,
produced: usize,
max: usize,
}
impl Decompressor {
pub fn new(config: MessageConfig) -> Result<Self, Error> {
let inflater = if config.context_takeover {
Inflater::new().with_window_bits(config.window_bits)?
} else {
Inflater::without_history(crate::Limits::unlimited())
.with_window_bits(config.window_bits)?
};
Ok(Self {
inflater,
takeover: config.context_takeover,
open: None,
})
}
pub fn decompress_message(
&mut self,
payload: &[u8],
out: &mut Vec<u8>,
max: usize,
) -> Result<usize, Error> {
self.decompress_fragment(payload, true, out, max)
}
pub fn decompress_fragment(
&mut self,
fragment: &[u8],
last: bool,
out: &mut Vec<u8>,
max: usize,
) -> Result<usize, Error> {
let result = self.fragment(fragment, last, out, max);
if result.is_err() {
self.open = None;
self.inflater.reset();
}
result
}
fn fragment(
&mut self,
fragment: &[u8],
last: bool,
out: &mut Vec<u8>,
max: usize,
) -> Result<usize, Error> {
let open = match self.open {
Some(m) => {
if out.len() != m.start + m.produced {
return Err(Error::config(ErrorKind::InvalidState));
}
m
}
None => OpenMessage {
start: out.len(),
produced: 0,
max,
},
};
let before = out.len();
let base = self.inflater.total_out() - open.produced as u64;
self.inflater
.set_max_output(base.saturating_add(open.max as u64));
let p = self
.inflater
.decompress_into(fragment, out, open.start, false)?;
if self.inflater.is_done() && fragment[p.consumed..].iter().any(|&b| b != 0) {
return Err(Error::new(
ErrorKind::TrailingData,
self.inflater.total_in(),
));
}
if last && !self.inflater.is_done() {
let t = self
.inflater
.decompress_into(&TAIL, out, open.start, false)?;
if t.consumed != TAIL.len() || !self.inflater.at_block_boundary() {
return Err(Error::new(
ErrorKind::UnexpectedEnd,
self.inflater.total_in(),
));
}
}
let produced = open.produced + (out.len() - before);
if !last {
self.open = Some(OpenMessage { produced, ..open });
return Ok(out.len() - before);
}
self.open = None;
if self.takeover {
self.inflater.remember(&out[open.start..]);
if self.inflater.is_done() {
self.inflater.next_stream()?;
}
} else {
self.inflater.reset();
}
Ok(out.len() - before)
}
pub fn wipe(&mut self) {
self.open = None;
self.inflater.wipe();
}
pub fn reset(&mut self) {
self.open = None;
self.inflater.reset();
}
#[must_use]
pub fn memory_in_use(&self) -> usize {
self.inflater.memory_in_use()
}
}