//! Deflate for what goes over the wire: large sync messages (`swarm::sync`). Automerge //! sends changes as they are stored, uncompressed; text and JSON in them shrink to about //! half. use std::io::{Read, Write}; use anyhow::{Result, bail}; use flate2::Compression; use flate2::read::DeflateDecoder; use flate2::write::DeflateEncoder; /// Below this, deflating saves too little to be worth it. pub(crate) const WORTH_FROM: usize = 1024; /// `bytes` deflated, if that makes them at least a tenth smaller. pub(crate) fn deflate(bytes: &[u8]) -> Option> { if bytes.len() < WORTH_FROM { return None; } let mut encoder = DeflateEncoder::new(Vec::with_capacity(bytes.len() / 2), Compression::fast()); encoder.write_all(bytes).ok()?; let deflated = encoder.finish().ok()?; (deflated.len() < bytes.len() - bytes.len() / 10).then_some(deflated) } /// `bytes` inflated, if they come to at most `limit` bytes: a small message can't make /// us hold a large one. pub(crate) fn inflate(bytes: &[u8], limit: usize) -> Result> { let mut inflated = Vec::with_capacity((bytes.len() * 3).min(limit)); DeflateDecoder::new(bytes) .take(limit as u64 + 1) .read_to_end(&mut inflated)?; if inflated.len() > limit { bail!("inflates to more than {limit} bytes"); } Ok(inflated) } #[cfg(test)] mod tests { use super::*; /// What deflates inflates back; little, or what doesn't shrink, isn't deflated; more /// than the limit is refused. #[test] fn deflates_and_inflates_within_limits() { let text = "a key, a value; ".repeat(1000); let deflated = deflate(text.as_bytes()).expect("text shrinks"); assert!(deflated.len() < text.len() / 4); assert_eq!(inflate(&deflated, text.len()).unwrap(), text.as_bytes()); assert!( inflate(&deflated, text.len() - 1).is_err(), "over the limit" ); assert!(deflate(b"short").is_none()); let mut rng = crate::testrand::Rng::new(1); let noise: Vec = (0..4096).map(|_| rng.next_u64() as u8).collect(); assert!(deflate(&noise).is_none(), "noise doesn't shrink"); } }