use std::env; use std::fs::OpenOptions; use std::io::Write; use zlib_rs::ReturnCode; use libz_rs_sys::z_stream; // This file accepts a stream of Vec gzipped chunks, returning chunks of (strings, bytes?). // For testing you can pass a filename on the cmdline to simulate chunking behavior. fn main() { let args: Vec = env::args().collect(); if args.len() != 2 {println!("Need XML filename"); return; } let filename = &args[1]; let output_filename = "output.xml"; // open output file for writing let mut out_file = OpenOptions::new() .create_new(true) .write(true) .open(&output_filename).expect("Could not open output file"); // read file let deflated = std::fs::read(&filename) .expect("Could not read file"); // divide file into chunks to pass to dechunker. Maybe 32k is a better option? let chunk_size = 65536; let chunks = deflated.as_slice().chunks(chunk_size); println!("\nHow many chunks? {:?}", chunks.len()); // initialize the libz_rs streamer let mut dechunker = init_dechunker(); let mut total_bytes_so_far: u64 = 0; let mut i= 0; for chunk in chunks { i += 1; // decompress each chunk let data = decompress_chunk(&mut dechunker, chunk, total_bytes_so_far, i); // keep track of how much we've received total_bytes_so_far += data.len() as u64; // write out the text out_file.write(&data).expect("Could not write contents"); } println!("Well done."); } fn init_dechunker() -> z_stream { let mut stream = libz_rs_sys::z_stream::default(); // initial the stream for chunking (that's the 16+15) let window_bits = 16+15; // the one true answer // this is based on zlib-rs crate --> fuzz/fuzz_targets/inflate_chunked.rs unsafe { let err = libz_rs_sys::inflateInit2_( &mut stream, window_bits as i32, libz_rs_sys::zlibVersion(), core::mem::size_of::() as i32, ); let return_code: ReturnCode = ReturnCode::from(err); assert_eq!(ReturnCode::Ok, return_code); }; // return the initialized stream stream } fn decompress_chunk(dechunker: &mut z_stream, chunk: &[u8], total_bytes_so_far: u64, i: i32) -> Vec { // this is based based on zlib-rs crate --> fuzz/fuzz_targets/inflate_chunked.rs let mut output = vec![0; 1_500_000]; dechunker.next_in = chunk.as_ptr() as *mut u8; dechunker.avail_in = chunk.len() as _; dechunker.next_out = output.as_mut_ptr(); dechunker.avail_out = output.len() as _; // ------------------------ let err = unsafe { libz_rs_sys::inflate(dechunker, ::libz_rs_sys::Z_NO_FLUSH) }; let return_code: ReturnCode = ReturnCode::from(err); match return_code { ReturnCode::Ok => { let num_bytes_this_result = dechunker.total_out - total_bytes_so_far; print!("\r{} {} {} ", i, num_bytes_this_result, dechunker.total_out); output.truncate(num_bytes_this_result.try_into().unwrap()); }, ReturnCode::StreamEnd => { // END: de-allocating all the libz unsafe stuff let num_bytes_this_result = dechunker.total_out - total_bytes_so_far; print!("\r{} {} {} ", i, num_bytes_this_result, dechunker.total_out); println!("\n----WE ARE AT THE END----"); output.truncate(num_bytes_this_result.try_into().unwrap()); unsafe { let err = libz_rs_sys::inflateEnd(dechunker); let return_code: ReturnCode = ReturnCode::from(err); assert_eq!(ReturnCode::Ok, return_code); } }, _ => { // ALL ERROR CONDITIONS: PANIC! if dechunker.msg.is_null() { panic!("CHUNK {}: {:?}: ", i, return_code) } else { let msg = unsafe { std::ffi::CStr::from_ptr(dechunker.msg) }; panic!("CHUNK {}: {:?}: {:?}", i, return_code, msg) } } }; output }