Skip to content

Commit a83478c

Browse files
authored
use only a single allocation for all deflate state (#429)
1 parent d7d7c1c commit a83478c

File tree

6 files changed

+345
-335
lines changed

6 files changed

+345
-335
lines changed

test-libz-rs-sys/src/deflate.rs

Lines changed: 162 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ use crate::assert_eq_rs_ng;
2121
const VERSION: *const c_char = libz_rs_sys::zlibVersion();
2222
const STREAM_SIZE: c_int = core::mem::size_of::<libz_rs_sys::z_stream>() as c_int;
2323

24+
const PAPER_100K: &[u8] = include_bytes!("test-data/paper-100k.pdf");
25+
const FIREWORKS: &[u8] = include_bytes!("test-data/fireworks.jpg");
26+
const LCET10: &str = include_str!("test-data/lcet10.txt");
27+
2428
pub mod quick {
2529
use super::*;
2630

@@ -1358,17 +1362,31 @@ fn test_deflate_hash_head_0() {
13581362
}
13591363

13601364
#[test]
1361-
fn test_deflate_copy() {
1362-
const HELLO: &str = "hello, hello!\0";
1365+
fn test_deflate_copy_small() {
1366+
test_deflate_copy_help("hello, hello!\0".as_bytes())
1367+
}
1368+
1369+
#[test]
1370+
#[cfg_attr(miri, ignore = "too slow")]
1371+
fn test_deflate_copy_large() {
1372+
test_deflate_copy_help(LCET10.as_bytes())
1373+
}
13631374

1375+
/// This test
1376+
///
1377+
/// - compresses half of the input, flushes, then compresses the remainder
1378+
/// - compresses half of the input, flushes, copies the state, then compresses
1379+
/// the remainder using the copy
1380+
///
1381+
/// It then asserts that the output is the same, testing that the copy was succesful.
1382+
fn test_deflate_copy_help(input: &[u8]) {
13641383
let config = DeflateConfig::default();
13651384

1366-
let mut strm = MaybeUninit::zeroed();
1385+
let _ = assert_eq_rs_ng!({
1386+
let mut strm_full = MaybeUninit::zeroed();
13671387

1368-
unsafe {
1369-
// first validate the config
1370-
let err = libz_rs_sys::deflateInit2_(
1371-
strm.as_mut_ptr(),
1388+
let err = deflateInit2_(
1389+
strm_full.as_mut_ptr(),
13721390
config.level,
13731391
config.method as i32,
13741392
config.window_bits,
@@ -1379,46 +1397,163 @@ fn test_deflate_copy() {
13791397
);
13801398
assert_eq!(err, 0);
13811399

1382-
let strm = strm.assume_init_mut();
1400+
let strm_full = strm_full.assume_init_mut();
13831401

1384-
let mut compr = [0; 32];
1385-
strm.next_in = HELLO.as_ptr() as *mut u8;
1386-
strm.next_out = compr.as_mut_ptr();
1402+
let mut full_out = vec![0u8; input.len() * 2 + 64];
13871403

1388-
for _ in HELLO.as_bytes() {
1389-
strm.avail_in = 1;
1390-
strm.avail_out = 1;
1404+
strm_full.next_out = full_out.as_mut_ptr();
1405+
strm_full.avail_out = full_out.len() as u32;
1406+
1407+
let half_full = input.len() / 2;
1408+
let mut pos_full = 0;
1409+
1410+
while pos_full < half_full {
1411+
strm_full.next_in = input.as_ptr().add(pos_full) as *mut u8;
1412+
let before_in = strm_full.total_in;
1413+
strm_full.avail_in = (half_full - pos_full) as u32;
13911414

1392-
let err = libz_rs_sys::deflate(strm, DeflateFlush::NoFlush as i32);
1415+
let err = deflate(strm_full, DeflateFlush::NoFlush as i32);
13931416
assert_eq!(err, 0);
1417+
assert_ne!(strm_full.avail_out, 0);
1418+
1419+
let used = (strm_full.total_in - before_in) as usize;
1420+
assert!(used > 0);
1421+
pos_full += used;
13941422
}
13951423

1396-
loop {
1397-
strm.avail_out = 1;
1398-
let err = libz_rs_sys::deflate(strm, DeflateFlush::Finish as i32);
1424+
strm_full.next_in = core::ptr::null_mut();
1425+
strm_full.avail_in = 0;
1426+
let err = deflate(strm_full, DeflateFlush::SyncFlush as i32);
1427+
assert_eq!(err, 0);
1428+
assert_ne!(strm_full.avail_out, 0);
1429+
1430+
while pos_full < input.len() {
1431+
strm_full.next_in = input.as_ptr().add(pos_full) as *mut u8;
1432+
let before_in = strm_full.total_in;
1433+
strm_full.avail_in = (input.len() - pos_full) as u32;
13991434

1435+
let err = deflate(strm_full, DeflateFlush::NoFlush as i32);
1436+
assert_eq!(err, 0);
1437+
assert_ne!(strm_full.avail_out, 0);
1438+
1439+
let used = (strm_full.total_in - before_in) as usize;
1440+
assert!(used > 0);
1441+
pos_full += used;
1442+
}
1443+
1444+
strm_full.next_in = core::ptr::null_mut();
1445+
strm_full.avail_in = 0;
1446+
let err = deflate(strm_full, DeflateFlush::SyncFlush as i32);
1447+
assert_eq!(err, 0);
1448+
assert_ne!(strm_full.avail_out, 0);
1449+
1450+
loop {
1451+
let err = deflate(strm_full, DeflateFlush::Finish as i32);
14001452
if ReturnCode::from(err) == ReturnCode::StreamEnd {
14011453
break;
14021454
}
1455+
assert_eq!(err, 0);
1456+
assert_ne!(strm_full.avail_out, 0);
1457+
}
1458+
1459+
let full_total_out = strm_full.total_out as usize;
1460+
let err = deflateEnd(strm_full);
1461+
assert_eq!(err, 0);
1462+
1463+
full_out.truncate(full_total_out);
1464+
1465+
let mut strm_half = MaybeUninit::zeroed();
1466+
1467+
let err = deflateInit2_(
1468+
strm_half.as_mut_ptr(),
1469+
config.level,
1470+
config.method as i32,
1471+
config.window_bits,
1472+
config.mem_level,
1473+
config.strategy as i32,
1474+
VERSION,
1475+
STREAM_SIZE,
1476+
);
1477+
assert_eq!(err, 0);
14031478

1479+
let strm_half = strm_half.assume_init_mut();
1480+
1481+
let mut copy_out = vec![0u8; input.len() * 2 + 64];
1482+
1483+
strm_half.next_out = copy_out.as_mut_ptr();
1484+
strm_half.avail_out = copy_out.len() as u32;
1485+
1486+
let half = input.len() / 2;
1487+
let mut pos = 0;
1488+
1489+
while pos < half {
1490+
strm_half.next_in = input.as_ptr().add(pos) as *mut u8;
1491+
let before_in = strm_half.total_in;
1492+
strm_half.avail_in = (half - pos) as u32;
1493+
1494+
let err = deflate(strm_half, DeflateFlush::NoFlush as i32);
14041495
assert_eq!(err, 0);
1496+
assert_ne!(strm_half.avail_out, 0);
1497+
1498+
let used = (strm_half.total_in - before_in) as usize;
1499+
assert!(used > 0);
1500+
pos += used;
14051501
}
14061502

1503+
strm_half.next_in = core::ptr::null_mut();
1504+
strm_half.avail_in = 0;
1505+
let err = deflate(strm_half, DeflateFlush::SyncFlush as i32);
1506+
assert_eq!(err, 0);
1507+
assert_ne!(strm_half.avail_out, 0);
1508+
14071509
let mut copy = MaybeUninit::uninit();
1408-
let err = libz_rs_sys::deflateCopy(copy.as_mut_ptr(), strm);
1510+
let err = deflateCopy(copy.as_mut_ptr(), strm_half);
14091511
assert_eq!(err, 0);
14101512

1411-
assert_eq!(
1412-
*(strm.state as *const u8),
1413-
*(((*copy.as_mut_ptr()).state) as *const u8),
1414-
);
1513+
let err = deflateEnd(strm_half);
1514+
assert_eq!(err, -3); // still partway through according to your original expectation
14151515

1416-
let err = libz_rs_sys::deflateEnd(strm);
1516+
let copy = copy.assume_init_mut();
1517+
1518+
while pos < input.len() {
1519+
copy.next_in = input.as_ptr().add(pos) as *mut u8;
1520+
let before_in = copy.total_in;
1521+
copy.avail_in = (input.len() - pos) as u32;
1522+
1523+
let err = deflate(copy, DeflateFlush::NoFlush as i32);
1524+
assert_eq!(err, 0);
1525+
assert_ne!(copy.avail_out, 0);
1526+
1527+
let used = (copy.total_in - before_in) as usize;
1528+
assert!(used > 0);
1529+
pos += used;
1530+
}
1531+
1532+
copy.next_in = core::ptr::null_mut();
1533+
copy.avail_in = 0;
1534+
let err = deflate(copy, DeflateFlush::SyncFlush as i32);
14171535
assert_eq!(err, 0);
1536+
assert_ne!(copy.avail_out, 0);
14181537

1419-
let err = libz_rs_sys::deflateEnd(copy.as_mut_ptr());
1538+
loop {
1539+
let err = deflate(copy, DeflateFlush::Finish as i32);
1540+
if ReturnCode::from(err) == ReturnCode::StreamEnd {
1541+
break;
1542+
}
1543+
assert_eq!(err, 0);
1544+
assert_ne!(copy.avail_out, 0);
1545+
}
1546+
1547+
let copy_total_out = copy.total_out as usize;
1548+
let err = deflateEnd(copy);
14201549
assert_eq!(err, 0);
1421-
}
1550+
1551+
copy_out.truncate(copy_total_out);
1552+
1553+
assert_eq!(full_out, copy_out);
1554+
1555+
full_out
1556+
});
14221557
}
14231558

14241559
#[test]
@@ -1642,6 +1777,7 @@ fn gzip_with_header() {
16421777
}
16431778

16441779
mod fuzz_based_tests {
1780+
use super::{FIREWORKS, LCET10, PAPER_100K};
16451781
use crate::helpers::compress_slice_ng;
16461782
use libz_rs_sys::gz_header;
16471783
use zlib_rs::{
@@ -1676,10 +1812,6 @@ mod fuzz_based_tests {
16761812
}
16771813
}
16781814

1679-
const PAPER_100K: &[u8] = include_bytes!("test-data/paper-100k.pdf");
1680-
const FIREWORKS: &[u8] = include_bytes!("test-data/fireworks.jpg");
1681-
const LCET10: &str = include_str!("test-data/lcet10.txt");
1682-
16831815
#[test]
16841816
#[cfg_attr(miri, ignore = "too slow")]
16851817
fn compress_lcet10() {

0 commit comments

Comments
 (0)