@@ -349,7 +349,6 @@ fn inf(input: &[u8], _what: &str, step: usize, win: i32, len: usize, err: c_int)
349349
350350 if matches ! ( ret, Z_NEED_DICT ) {
351351 let ret = unsafe { inflateSetDictionary ( & mut stream, input. as_ptr ( ) , 1 ) } ;
352- println ! ( "{ret:?}" ) ;
353352 assert_eq ! ( ret, Z_DATA_ERROR ) ;
354353
355354 unsafe { set_mode_dict ( & mut stream) }
@@ -2705,3 +2704,121 @@ fn cover_back() {
27052704 assert_eq ! ( ret, Z_OK ) ;
27062705 }
27072706}
2707+
2708+ #[ test]
2709+ fn inflate_copy_after_half_input ( ) {
2710+ let input = include_bytes ! ( "test-data/compression-corpus/The fastest WASM zlib.md.gzip-9.gz" ) ;
2711+
2712+ let _ = assert_eq_rs_ng ! ( {
2713+ let mut stream = MaybeUninit :: <z_stream>:: zeroed( ) ;
2714+ let ret = unsafe {
2715+ inflateInit2_(
2716+ stream. as_mut_ptr( ) ,
2717+ 16 + 15 ,
2718+ zlibVersion( ) ,
2719+ core:: mem:: size_of:: <z_stream>( ) as i32 ,
2720+ )
2721+ } ;
2722+ let stream = stream. assume_init_mut( ) ;
2723+ assert_eq!( ret, Z_OK ) ;
2724+
2725+ let mut out = vec![ 0u8 ; 16 * 1024 ] ;
2726+
2727+ // First, decompress only the first half of the compressed input.
2728+ let half = input. len( ) / 2 ;
2729+
2730+ stream. next_in = input. as_ptr( ) as * mut _;
2731+ stream. avail_in = half as _;
2732+ stream. next_out = out. as_mut_ptr( ) ;
2733+ stream. avail_out = out. len( ) as _;
2734+
2735+ loop {
2736+ let ret = unsafe { inflate( stream, InflateFlush :: NoFlush as _) } ;
2737+
2738+ assert!(
2739+ matches!( ret, Z_OK | Z_BUF_ERROR | Z_STREAM_END ) ,
2740+ "unexpected inflate return: {}" ,
2741+ ret
2742+ ) ;
2743+
2744+ if matches!( ret, Z_STREAM_END ) {
2745+ unreachable!( "we only provide half of the input" )
2746+ }
2747+
2748+ if stream. avail_in == 0 {
2749+ break ;
2750+ }
2751+
2752+ if stream. avail_out == 0 {
2753+ unreachable!( "there is enough output space" )
2754+ }
2755+ }
2756+
2757+ // At this point, we’ve decompressed part (or possibly all) of the stream.
2758+ let prefix_len = stream. total_out as usize ;
2759+
2760+ // Make a copy of the inflate state *after* half the input has been processed.
2761+ let mut copy = MaybeUninit :: <z_stream>:: zeroed( ) ;
2762+ let ret = unsafe { inflateCopy( copy. as_mut_ptr( ) , stream) } ;
2763+ let copy = copy. assume_init_mut( ) ;
2764+ assert_eq!( ret, Z_OK ) ;
2765+
2766+ // Prepare a separate output buffer for the copy, and copy the already-produced prefix
2767+ let mut out_copy = vec![ 0u8 ; 16 * 1024 ] ;
2768+ out_copy[ ..prefix_len] . copy_from_slice( & out[ ..prefix_len] ) ;
2769+
2770+ // The original stream already has next_out pointing at out[prefix_len].
2771+ // For the copy, we need to point it at the corresponding location in out_copy.
2772+ copy. next_out = unsafe { out_copy. as_mut_ptr( ) . add( prefix_len) } ;
2773+ copy. avail_out = ( out_copy. len( ) - prefix_len) as _;
2774+
2775+ // Now feed the *remainder* of the compressed input to both streams.
2776+ let remaining = input. len( ) - half;
2777+ if remaining > 0 {
2778+ stream. next_in = unsafe { input. as_ptr( ) . add( half) as * mut _ } ;
2779+ stream. avail_in = remaining as _;
2780+
2781+ copy. next_in = stream. next_in;
2782+ copy. avail_in = stream. avail_in;
2783+ }
2784+
2785+ // Decompress the remainder in lockstep on both the original and the copy.
2786+ loop {
2787+ let ret1 = unsafe { inflate( stream, InflateFlush :: NoFlush as _) } ;
2788+ let ret2 = unsafe { inflate( copy, InflateFlush :: NoFlush as _) } ;
2789+
2790+ // Both streams should behave identically
2791+ assert_eq!( ret1, ret2) ;
2792+ assert!(
2793+ matches!( ret1, Z_OK | Z_BUF_ERROR | Z_STREAM_END ) ,
2794+ "unexpected inflate return: {ret}" ,
2795+ ) ;
2796+
2797+ // Their accounting should remain in sync at all times
2798+ assert_eq!( stream. total_out, copy. total_out) ;
2799+ assert_eq!( stream. total_in, copy. total_in) ;
2800+
2801+ if matches!( ret1, Z_STREAM_END ) {
2802+ break ;
2803+ }
2804+
2805+ // If we somehow run out of input or output space, bail
2806+ if stream. avail_in == 0 && copy. avail_in == 0 {
2807+ break ;
2808+ }
2809+ if stream. avail_out == 0 || copy. avail_out == 0 {
2810+ break ;
2811+ }
2812+ }
2813+
2814+ assert_eq!( unsafe { inflateEnd( stream) } , Z_OK ) ;
2815+ assert_eq!( unsafe { inflateEnd( copy) } , Z_OK ) ;
2816+
2817+ let total = stream. total_out as usize ;
2818+ assert_eq!( total, copy. total_out as usize ) ;
2819+
2820+ assert_eq!( & out[ ..total] , & out_copy[ ..total] ) ;
2821+
2822+ out[ ..total] . to_vec( )
2823+ } ) ;
2824+ }
0 commit comments