1use bytes::Bytes;
34use flate2::{Compress, Decompress, FlushCompress, FlushDecompress, Status};
35
36pub const DEFAULT_LEVEL: u32 = 6;
39
40pub const DEFAULT_MAX_FRAME_SIZE: u64 = 64 * 1024 * 1024;
42
43const SYNC_FLUSH_TAIL: [u8; 4] = [0x00, 0x00, 0xff, 0xff];
45
46const CHUNK: usize = 8 * 1024;
48
49#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
51#[non_exhaustive]
52pub enum Error {
53 #[error("decompression failed")]
55 Decompress,
56
57 #[error("decompressed frame exceeded {0} bytes")]
59 TooLarge(u64),
60}
61
62pub type Result<T> = std::result::Result<T, Error>;
64
65pub struct Encoder(Compress);
68
69impl Encoder {
70 pub fn new() -> Self {
72 Self::with_level(DEFAULT_LEVEL)
73 }
74
75 pub fn with_level(level: u32) -> Self {
78 Self(Compress::new(flate2::Compression::new(level.min(9)), false))
80 }
81
82 pub fn frame(&mut self, payload: &[u8]) -> Bytes {
86 if payload.is_empty() {
87 return Bytes::new();
88 }
89
90 let mut out = Vec::with_capacity(payload.len() / 2 + 16);
91 let mut tmp = [0u8; CHUNK];
92 let mut input = payload;
93
94 loop {
97 let before_in = self.0.total_in();
98 let before_out = self.0.total_out();
99 self.0.compress(input, &mut tmp, FlushCompress::Sync).expect("deflate");
100 let consumed = (self.0.total_in() - before_in) as usize;
101 let produced = (self.0.total_out() - before_out) as usize;
102 out.extend_from_slice(&tmp[..produced]);
103 input = &input[consumed..];
104 if produced < tmp.len() {
105 break;
106 }
107 }
108
109 debug_assert!(
111 out.ends_with(&SYNC_FLUSH_TAIL),
112 "a sync flush must end in the deflate marker"
113 );
114 out.truncate(out.len() - SYNC_FLUSH_TAIL.len());
115 Bytes::from(out)
116 }
117}
118
119impl Default for Encoder {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125pub struct Decoder {
128 inner: Decompress,
129 max_frame_size: u64,
130}
131
132impl Decoder {
133 pub fn new() -> Self {
135 Self::with_max_frame_size(DEFAULT_MAX_FRAME_SIZE)
136 }
137
138 pub fn with_max_frame_size(max_frame_size: u64) -> Self {
143 Self {
145 inner: Decompress::new(false),
146 max_frame_size,
147 }
148 }
149
150 pub fn frame(&mut self, slice: &[u8]) -> Result<Bytes> {
156 let mut out = Vec::new();
157 self.frame_into(slice, &mut out)?;
158 Ok(Bytes::from(out))
159 }
160
161 pub fn frame_into(&mut self, slice: &[u8], out: &mut Vec<u8>) -> Result<()> {
165 out.clear();
166 if slice.is_empty() {
167 return Ok(());
168 }
169 let mut tmp = [0u8; CHUNK];
170
171 for segment in [slice, &SYNC_FLUSH_TAIL] {
174 let mut input = segment;
175 loop {
176 let before_in = self.inner.total_in();
177 let before_out = self.inner.total_out();
178 let status = self
179 .inner
180 .decompress(input, &mut tmp, FlushDecompress::Sync)
181 .map_err(|_| Error::Decompress)?;
182 let consumed = (self.inner.total_in() - before_in) as usize;
183 let produced = (self.inner.total_out() - before_out) as usize;
184 if out.len() as u64 + produced as u64 > self.max_frame_size {
186 return Err(Error::TooLarge(self.max_frame_size));
187 }
188 out.extend_from_slice(&tmp[..produced]);
189 input = &input[consumed..];
190
191 if matches!(status, Status::StreamEnd) || (input.is_empty() && produced < tmp.len()) {
194 break;
195 }
196 if consumed == 0 && produced == 0 {
197 break;
198 }
199 }
200 }
201
202 Ok(())
203 }
204}
205
206impl Default for Decoder {
207 fn default() -> Self {
208 Self::new()
209 }
210}
211
212#[cfg(test)]
213mod test {
214 use super::*;
215
216 fn roundtrip(frames: &[&[u8]]) -> Vec<Vec<u8>> {
218 let mut enc = Encoder::new();
219 let slices: Vec<Bytes> = frames.iter().map(|f| enc.frame(f)).collect();
220
221 let mut dec = Decoder::new();
222 slices.iter().map(|s| dec.frame(s).unwrap().to_vec()).collect()
223 }
224
225 #[test]
226 fn stream_roundtrip() {
227 let frames: &[&[u8]] = &[b"the quick brown fox", b"the quick brown dog", b"the lazy fox"];
228 let got = roundtrip(frames);
229 for (a, b) in frames.iter().zip(&got) {
230 assert_eq!(*a, b.as_slice());
231 }
232 }
233
234 #[test]
235 fn frame_into_reuses_its_output_buffer() {
236 let mut encoder = Encoder::new();
237 let frames = [b"first payload".as_slice(), b"second payload".as_slice()];
238 let mut decoder = Decoder::new();
239 let mut out = Vec::with_capacity(64);
240 let ptr = out.as_ptr();
241 for frame in frames {
242 decoder.frame_into(&encoder.frame(frame), &mut out).unwrap();
243 assert_eq!(out, frame);
244 assert_eq!(out.as_ptr(), ptr);
245 }
246 decoder.frame_into(b"", &mut out).unwrap();
247 assert!(out.is_empty());
248 }
249
250 #[test]
251 fn empty_frames_roundtrip() {
252 assert!(Encoder::new().frame(b"").is_empty());
253 assert!(Decoder::new().frame(b"").unwrap().is_empty());
254 }
255
256 #[test]
257 fn cross_frame_context_shrinks() {
258 let payload = b"Media over QUIC delivers real-time latency at massive scale.".repeat(6);
261 let mut enc = Encoder::new();
262 let first = enc.frame(&payload);
263 let second = enc.frame(&payload);
264 assert!(
265 second.len() < first.len(),
266 "repeat frame {} should be smaller than first {}",
267 second.len(),
268 first.len()
269 );
270 }
271
272 #[test]
273 fn frame_larger_than_chunk_roundtrips() {
274 let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
277 let payload: Vec<u8> = (0..64 * 1024)
278 .map(|_| {
279 state ^= state << 13;
280 state ^= state >> 7;
281 state ^= state << 17;
282 (state >> 56) as u8
283 })
284 .collect();
285
286 let mut enc = Encoder::new();
287 let slice = enc.frame(&payload);
288 assert!(slice.len() > CHUNK, "slice {} should exceed CHUNK {CHUNK}", slice.len());
289
290 let mut dec = Decoder::new();
291 assert_eq!(dec.frame(&slice).unwrap(), Bytes::from(payload));
292 }
293
294 #[test]
295 fn decompress_rejects_garbage() {
296 let mut dec = Decoder::new();
297 assert_eq!(dec.frame(b"not a deflate stream at all"), Err(Error::Decompress));
298 }
299
300 #[test]
301 fn enforces_max_frame_size() {
302 let payload = vec![0u8; 1024];
304 let slice = Encoder::new().frame(&payload);
305
306 let mut dec = Decoder::with_max_frame_size(512);
307 assert_eq!(dec.frame(&slice), Err(Error::TooLarge(512)));
308 }
309
310 #[test]
311 fn custom_level_roundtrips() {
312 let payload = b"compress me at maximum effort".repeat(8);
313 let mut enc = Encoder::with_level(9);
314 let slice = enc.frame(&payload);
315 let mut dec = Decoder::new();
316 assert_eq!(dec.frame(&slice).unwrap(), Bytes::from(payload));
317 }
318}