use flate2::{Compress, Compression, FlushCompress, Status};
pub const WINDOW_BITS_DEFAULT: u8 = 15;
pub const WINDOW_BITS_MIN: u8 = 9;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeflateError {
Encode(&'static str),
Config(&'static str),
ZipLimit(&'static str),
TooManyPieces {
what: &'static str,
total: usize,
piece: usize,
pieces: usize,
max: usize,
},
}
impl std::fmt::Display for DeflateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DeflateError::Encode(m) => write!(f, "deflate encode failed: {m}"),
DeflateError::Config(m) => write!(f, "bad ldeflate config: {m}"),
DeflateError::ZipLimit(m) => write!(f, "ldeflate zip limit: {m}"),
DeflateError::TooManyPieces {
what,
total,
piece,
pieces,
max,
} => write!(
f,
"ldeflate: {total} bytes at {piece} bytes per piece is {pieces} pieces \
of {what}, over the ceiling of {max}; pass at least {} bytes per piece",
crate::limits::min_piece_for(*total),
),
}
}
}
impl std::error::Error for DeflateError {}
pub struct Encoder {
z: Compress,
level: u32,
zlib_wrapped: bool,
}
impl Encoder {
pub fn new(level: u32, zlib_wrapped: bool) -> Result<Self, DeflateError> {
Self::new_with_window(level, zlib_wrapped, WINDOW_BITS_DEFAULT)
}
pub fn new_with_window(
level: u32,
zlib_wrapped: bool,
window_bits: u8,
) -> Result<Self, DeflateError> {
if level > 9 {
return Err(DeflateError::Config("level must be 0..=9"));
}
if !(WINDOW_BITS_MIN..=WINDOW_BITS_DEFAULT).contains(&window_bits) {
return Err(DeflateError::Config("window_bits must be 9..=15"));
}
Ok(Encoder {
z: Compress::new_with_window_bits(Compression::new(level), zlib_wrapped, window_bits),
level,
zlib_wrapped,
})
}
#[inline]
pub fn level(&self) -> u32 {
self.level
}
pub fn compress_into(&mut self, src: &[u8], out: &mut Vec<u8>) -> Result<usize, DeflateError> {
self.z.reset();
self.drive(src, out, FlushCompress::Finish)
}
pub fn compress_segment_into(
&mut self,
src: &[u8],
out: &mut Vec<u8>,
) -> Result<usize, DeflateError> {
self.z.reset();
self.drive(src, out, FlushCompress::Full)
}
#[inline]
pub fn is_zlib_wrapped(&self) -> bool {
self.zlib_wrapped
}
fn drive(
&mut self,
src: &[u8],
out: &mut Vec<u8>,
end: FlushCompress,
) -> Result<usize, DeflateError> {
let start = out.len();
out.reserve((src.len() / 2).max(64) + 64);
let mut consumed = 0usize;
loop {
let in_before = self.z.total_in();
let out_before = self.z.total_out();
if out.len() == out.capacity() {
out.reserve(out.capacity().max(1024));
}
let spare = out.capacity() - out.len();
let write_at = out.len();
let dst =
unsafe { std::slice::from_raw_parts_mut(out.as_mut_ptr().add(write_at), spare) };
let remaining = &src[consumed..];
let flush = if consumed == src.len() {
end
} else {
FlushCompress::None
};
let status = self
.z
.compress(remaining, dst, flush)
.map_err(|_| DeflateError::Encode("compressor returned an error"))?;
let produced = (self.z.total_out() - out_before) as usize;
let took = (self.z.total_in() - in_before) as usize;
unsafe { out.set_len(write_at + produced) };
consumed += took;
match status {
Status::StreamEnd => break,
Status::Ok | Status::BufError => {
if consumed == src.len() && produced == 0 && took == 0 {
if matches!(end, FlushCompress::Finish) {
return Err(DeflateError::Encode(
"compressor stalled before StreamEnd",
));
}
break;
}
if produced == spare {
out.reserve(spare.max(1024));
}
}
}
}
Ok(out.len() - start)
}
}
pub struct EncoderPool {
free: std::sync::Mutex<Vec<Encoder>>,
built: std::sync::atomic::AtomicUsize,
level: u32,
zlib_wrapped: bool,
window_bits: u8,
}
impl EncoderPool {
pub fn new(level: u32, zlib_wrapped: bool, window_bits: u8) -> Self {
EncoderPool {
free: std::sync::Mutex::new(Vec::new()),
built: std::sync::atomic::AtomicUsize::new(0),
level,
zlib_wrapped,
window_bits,
}
}
pub fn checkout(&self) -> Result<PooledEncoder<'_>, DeflateError> {
let taken = self
.free
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.pop();
let enc = match taken {
Some(e) => e,
None => {
self.built
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Encoder::new_with_window(self.level, self.zlib_wrapped, self.window_bits)?
}
};
Ok(PooledEncoder {
pool: self,
enc: Some(enc),
})
}
pub fn parked(&self) -> usize {
self.free
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.len()
}
pub fn constructed(&self) -> usize {
self.built.load(std::sync::atomic::Ordering::Relaxed)
}
}
pub struct PooledEncoder<'a> {
pool: &'a EncoderPool,
enc: Option<Encoder>,
}
impl std::ops::Deref for PooledEncoder<'_> {
type Target = Encoder;
fn deref(&self) -> &Encoder {
self.enc.as_ref().expect("live until drop")
}
}
impl std::ops::DerefMut for PooledEncoder<'_> {
fn deref_mut(&mut self) -> &mut Encoder {
self.enc.as_mut().expect("live until drop")
}
}
impl Drop for PooledEncoder<'_> {
fn drop(&mut self) {
if let Some(e) = self.enc.take() {
self.pool
.free
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.push(e);
}
}
}
pub fn zlib_compress(src: &[u8], level: u32) -> Result<Vec<u8>, DeflateError> {
let mut out = Vec::new();
Encoder::new(level, true)?.compress_into(src, &mut out)?;
Ok(out)
}
pub fn deflate_compress(src: &[u8], level: u32) -> Result<Vec<u8>, DeflateError> {
let mut out = Vec::new();
Encoder::new(level, false)?.compress_into(src, &mut out)?;
Ok(out)
}
const NMAX: usize = 5552;
const BASE: u32 = 65521;
pub fn adler32(adler: u32, data: &[u8]) -> u32 {
let mut s1 = adler & 0xffff;
let mut s2 = (adler >> 16) & 0xffff;
for block in data.chunks(NMAX) {
for &b in block {
s1 += b as u32;
s2 += s1;
}
s1 %= BASE;
s2 %= BASE;
}
(s2 << 16) | s1
}
pub fn adler32_combine(a: u32, b: u32, len_b: u64) -> u32 {
let rem = (len_b % BASE as u64) as u32;
let a1 = a & 0xffff;
let a2 = (a >> 16) & 0xffff;
let b1 = b & 0xffff;
let b2 = (b >> 16) & 0xffff;
let mut sum1 = a1 as u64;
let mut sum2 = (rem as u64 * a1 as u64) % BASE as u64;
sum1 += b1 as u64 + BASE as u64 - 1;
sum2 += a2 as u64 + b2 as u64 + BASE as u64 - rem as u64;
if sum1 >= BASE as u64 {
sum1 -= BASE as u64;
}
if sum1 >= BASE as u64 {
sum1 -= BASE as u64;
}
if sum2 >= (BASE as u64) << 1 {
sum2 -= (BASE as u64) << 1;
}
if sum2 >= BASE as u64 {
sum2 -= BASE as u64;
}
(sum1 as u32) | ((sum2 as u32) << 16)
}
pub fn zlib_header(level: u32) -> [u8; 2] {
let cmf: u32 = 0x78;
let flevel: u32 = match level {
0..=1 => 0,
2..=5 => 1,
6 => 2,
_ => 3,
};
let mut flg = flevel << 6;
flg += 31 - ((cmf << 8 | flg) % 31);
[cmf as u8, flg as u8]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zlib_header_matches_the_real_encoder_at_every_level() {
for level in 0..=9u32 {
let real = zlib_compress(b"the quick brown fox", level).unwrap();
let ours = zlib_header(level);
assert_eq!(
&real[..2],
&ours[..],
"level {level}: hand-written zlib header {ours:02x?} != encoder's {:02x?}",
&real[..2]
);
let word = (ours[0] as u32) << 8 | ours[1] as u32;
assert_eq!(word % 31, 0, "level {level}: header fails the %31 check");
}
}
#[test]
fn adler_combine_equals_whole_buffer_adler() {
let data: Vec<u8> = (0..40_000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 13) as u8)
.collect();
let whole = adler32(1, &data);
for &cut in &[0usize, 1, 2, 5551, 5552, 5553, 12_345, 39_999, 40_000] {
let (a, b) = data.split_at(cut);
let combined = adler32_combine(adler32(1, a), adler32(1, b), b.len() as u64);
assert_eq!(
combined, whole,
"combine at cut={cut} gave {combined:08x}, whole is {whole:08x}"
);
}
let mut acc = adler32(1, &data[..1000]);
acc = adler32_combine(acc, adler32(1, &data[1000..25_000]), 24_000);
acc = adler32_combine(acc, adler32(1, &data[25_000..]), 15_000);
assert_eq!(acc, whole, "left-folded 3-way combine must equal the whole");
}
#[test]
fn the_pool_actually_reuses_and_the_check_can_see_the_difference() {
let pool = EncoderPool::new(6, true, WINDOW_BITS_DEFAULT);
assert_eq!(
pool.constructed(),
0,
"the pool must build nothing until asked"
);
assert_eq!(pool.parked(), 0);
let mut out = Vec::new();
{
let mut e = pool.checkout().unwrap();
e.compress_into(b"the quick brown fox", &mut out).unwrap();
}
assert_eq!(
pool.constructed(),
1,
"the first checkout must build exactly one"
);
assert_eq!(
pool.parked(),
1,
"dropping the guard must return the encoder"
);
for i in 0..20 {
let mut e = pool.checkout().unwrap();
out.clear();
e.compress_into(b"another object entirely", &mut out)
.unwrap();
drop(e);
assert_eq!(
pool.constructed(),
1,
"checkout {i} constructed a new encoder instead of reusing the parked one — \
the pool is buying nothing"
);
}
{
let _a = pool.checkout().unwrap();
let _b = pool.checkout().unwrap();
assert_eq!(
pool.constructed(),
2,
"the second concurrent guard needs its own"
);
assert_eq!(pool.parked(), 0);
}
assert_eq!(pool.parked(), 2, "both guards must return their encoder");
}
#[test]
fn the_serial_scheduler_builds_one_encoder_for_the_whole_call() {
use crate::plan::{Config, compress_objects_serial};
let data: Vec<Vec<u8>> = (0..4000)
.map(|i| format!("object {i} ").repeat(30).into_bytes())
.collect();
let r: Vec<&[u8]> = data.iter().map(|v| v.as_slice()).collect();
let cfg = Config::default().without_coalescing();
let out = compress_objects_serial(&r, &cfg).unwrap();
assert_eq!(out.len(), data.len());
let coalesced = compress_objects_serial(&r, &Config::default()).unwrap();
assert_eq!(out, coalesced, "coalescing must not change one output byte");
}
#[test]
fn a_recycled_encoder_still_starts_a_clean_stream_every_time() {
use std::io::Read;
let pool = EncoderPool::new(6, true, WINDOW_BITS_DEFAULT);
let corpus: Vec<Vec<u8>> = (0..64)
.map(|i| {
let tag = format!("object-{i}-");
let mut v = Vec::new();
while v.len() < 40 + i * 137 {
v.extend_from_slice(tag.as_bytes());
}
v
})
.collect();
for (i, src) in corpus.iter().enumerate() {
let mut z = Vec::new();
pool.checkout().unwrap().compress_into(src, &mut z).unwrap();
assert_eq!(
z,
zlib_compress(src, 6).unwrap(),
"object {i}: a recycled encoder produced different bytes from a fresh one"
);
let mut got = Vec::new();
flate2::read::ZlibDecoder::new(&z[..])
.read_to_end(&mut got)
.unwrap();
assert_eq!(&got, src, "object {i} round-trip differs");
}
}
#[test]
fn adler32_known_answers() {
assert_eq!(adler32(1, b""), 1);
assert_eq!(adler32(1, b"a"), 0x00620062);
assert_eq!(adler32(1, b"abc"), 0x024d0127);
assert_eq!(adler32(1, b"Wikipedia"), 0x11E60398);
let big = vec![0xffu8; 20_000];
let mut ref_s1: u64 = 1;
let mut ref_s2: u64 = 0;
for &b in &big {
ref_s1 = (ref_s1 + b as u64) % BASE as u64;
ref_s2 = (ref_s2 + ref_s1) % BASE as u64;
}
assert_eq!(adler32(1, &big), ((ref_s2 as u32) << 16) | ref_s1 as u32);
}
#[test]
fn kernel_roundtrips_through_both_decoders() {
let corpus: Vec<Vec<u8>> = vec![
Vec::new(),
b"x".to_vec(),
b"the quick brown fox jumps over the lazy dog".repeat(200),
(0..70_000u32).map(|i| (i % 251) as u8).collect(),
(0..200_000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 11) as u8)
.collect(),
];
for (n, src) in corpus.iter().enumerate() {
for level in [0u32, 1, 6, 9] {
let z = zlib_compress(src, level).unwrap();
let mut got = Vec::new();
use std::io::Read;
flate2::read::ZlibDecoder::new(&z[..])
.read_to_end(&mut got)
.unwrap();
assert_eq!(
&got, src,
"corpus {n} level {level}: flate2 round-trip differs"
);
if !src.is_empty() {
let raw = &z[2..z.len() - 4];
let mut buf = vec![0u8; src.len() + linflate::OVERWRITE_HEADROOM];
let wrote = linflate::inflate_into(raw, &mut buf).unwrap();
assert_eq!(
&buf[..wrote],
&src[..],
"corpus {n} level {level}: linflate differs"
);
}
}
}
}
}