use std::{mem, num};
use compress::Gzip;
use miniz_oxide::deflate::core::{
CompressorOxide, TDEFLFlush, TDEFLStatus, compress, create_comp_flags_from_zip_params,
};
use o3::{
alloc,
buffer::{self, pool},
};
use crate::payload::{compress, crc32::Hasher};
const HEADER: usize = 10;
const TRAILER: usize = 8;
pub(super) const FRAMING: usize = HEADER + TRAILER;
const LEVEL: i32 = 3;
pub(super) struct Encoder(Box<CompressorOxide>, Hasher);
impl Encoder {
pub(super) fn try_new() -> Result<Self, alloc::Error> {
let flags = create_comp_flags_from_zip_params(LEVEL, 0, 0);
Ok(Self(
alloc::BoxExt::try_new(CompressorOxide::new(flags))?,
Hasher::new(),
))
}
pub(super) const fn bound(&self, len: usize) -> usize {
HEADER + len + (len >> 12) + (len >> 14) + (len >> 25) + 13 + TRAILER
}
pub(super) fn encode<'a>(
&mut self,
src: &[u8],
mut output: buffer::raw::Output<
'a,
pool::Fixed<{ Gzip::SLOTS.get() }, { Gzip::CAPACITY.get() }>,
>,
) -> Option<pool::Complete<'a, pool::Fixed<{ Gzip::SLOTS.get() }, { Gzip::CAPACITY.get() }>>>
{
let capacity = output.len();
let bytes = output.as_mut_slice();
let room = capacity.checked_sub(HEADER + TRAILER)?;
self.0.reset();
let (status, read, wrote) = compress(
&mut self.0,
src,
&mut bytes[HEADER..HEADER + room],
TDEFLFlush::Finish,
);
if status != TDEFLStatus::Done || read != src.len() {
return None;
}
let written = wrote
.checked_add(HEADER + TRAILER)
.filter(|written| *written <= capacity && *written < src.len())
.and_then(num::NonZero::new)?;
bytes[..HEADER].copy_from_slice(&[0x1f, 0x8b, 0x08, 0, 0, 0, 0, 0, 0, 0xff]);
let end = written.get() - TRAILER;
let mut checksum = self.1.clone();
checksum.update(src);
bytes[end..end + 4].copy_from_slice(&checksum.finalize().to_le_bytes());
unsafe { bytes.get_unchecked_mut(end + 4..written.get()) }
.copy_from_slice(&(src.len() as u32).to_le_bytes());
Some(unsafe { output.complete(written) })
}
}
const _: () = assert!(mem::size_of::<Encoder>() == 2 * mem::size_of::<usize>());