Skip to content

Commit 24e2c37

Browse files
committed
fix for latest minz_oxide
1 parent 9c86cb8 commit 24e2c37

1 file changed

Lines changed: 60 additions & 3 deletions

File tree

russh/src/compression.rs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,45 @@ mod tests {
217217
assert!(decompressed.is_empty());
218218
}
219219

220+
#[test]
221+
fn incompressible_packets_round_trip() {
222+
// Incompressible input makes deflate output land on or past the
223+
// `input.len() + 10` reservation, so the compress loops must keep
224+
// going when the buffer comes back exactly full, on both `compress`
225+
// and `compress_into`.
226+
let mut comp = Compress::None;
227+
let mut decomp = Decompress::None;
228+
Compression::Zlib.init_compress(&mut comp);
229+
Compression::Zlib.init_decompress(&mut decomp);
230+
231+
let mut seed = 0x9e37_79b9_7f4a_7c15_u64;
232+
for &len in &[1usize, 100, 4096, 32768, 65536, 200_000, 262_144] {
233+
let payload: Vec<u8> = (0..len)
234+
.map(|_| {
235+
seed ^= seed << 13;
236+
seed ^= seed >> 7;
237+
seed ^= seed << 17;
238+
(seed >> 24) as u8
239+
})
240+
.collect();
241+
242+
let mut cbuf = Vec::new();
243+
let compressed = comp.compress(&payload, &mut cbuf).unwrap().to_vec();
244+
let mut dbuf = Vec::new();
245+
let out = decomp.decompress(&compressed, &mut dbuf).unwrap();
246+
assert_eq!(out, payload.as_slice(), "compress: {len} bytes");
247+
248+
let prefix = b"hdr".len();
249+
let mut cbuf = b"hdr".to_vec();
250+
let n = comp.compress_into(&payload, &mut cbuf, prefix).unwrap();
251+
assert_eq!(&cbuf[..prefix], b"hdr");
252+
assert_eq!(cbuf.len(), prefix + n);
253+
let mut dbuf = Vec::new();
254+
let out = decomp.decompress(&cbuf[prefix..], &mut dbuf).unwrap();
255+
assert_eq!(out, payload.as_slice(), "compress_into: {len} bytes");
256+
}
257+
}
258+
220259
#[test]
221260
fn partial_flush_packets_round_trip() {
222261
// Real SSH packets are partial flushes on one continuous stream, not
@@ -262,10 +301,17 @@ impl Compress {
262301
let n_out_ = z.total_out() as usize - n_out;
263302
#[allow(clippy::indexing_slicing)] // length checked
264303
let c = z.compress(&input[n_in_..], &mut output[n_out_..], flush)?;
304+
// A flush is complete only once deflate leaves room in
305+
// the output buffer; `Ok` with the buffer exactly full
306+
// means more is pending.
307+
let output_full = z.total_out() as usize - n_out == output.len();
265308
match c {
266309
flate2::Status::BufError => {
267310
output.resize(output.len() * 2, 0);
268311
}
312+
_ if output_full => {
313+
output.resize(output.len() * 2, 0);
314+
}
269315
_ => break,
270316
}
271317
}
@@ -300,11 +346,17 @@ impl Compress {
300346
let n_out_ = z.total_out() as usize - n_out;
301347
#[allow(clippy::indexing_slicing)] // length checked
302348
let c = z.compress(&input[n_in_..], &mut output[start_len + n_out_..], flush)?;
349+
// See `compress`: full buffer on `Ok` means more pending.
350+
let output_full = start_len + (z.total_out() as usize - n_out) == output.len();
303351
match c {
304352
flate2::Status::BufError => {
305353
let growth = output.len().saturating_sub(start_len).max(1);
306354
output.resize(output.len() + growth, 0);
307355
}
356+
_ if output_full => {
357+
let growth = output.len().saturating_sub(start_len).max(1);
358+
output.resize(output.len() + growth, 0);
359+
}
308360
_ => break,
309361
}
310362
}
@@ -341,11 +393,16 @@ impl Decompress {
341393
let d = z.decompress(&input[n_in_..], &mut output[n_out_..], flush);
342394
match d? {
343395
flate2::Status::Ok | flate2::Status::BufError => {
344-
let consumed_all_input =
345-
z.total_in() as usize - n_in == input.len();
396+
let made_progress = z.total_in() as usize - n_in != n_in_
397+
|| z.total_out() as usize - n_out != n_out_;
346398
let output_full = z.total_out() as usize - n_out == output.len();
347399

348-
if !output_full && consumed_all_input {
400+
// Keep going while calls still produce output: the
401+
// inflater can return `BufError` with all input
402+
// consumed and decoded bytes still buffered inside
403+
// it (miniz_oxide >= 0.9 does exactly that). Only
404+
// a call that moves nothing means we're done.
405+
if !output_full && !made_progress {
349406
break;
350407
}
351408

0 commit comments

Comments
 (0)