diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 982933b..c804c53 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -1195,6 +1195,13 @@ zstream_run_try(VALUE value_arg) } } + if (err == Z_BUF_ERROR && z->func == &deflate_funcs && z->stream.avail_in == 0) { + /* deflate() reports Z_BUF_ERROR for a flush when no input was consumed + * since the previous flush of the same or higher rank. Nothing is lost, + * so treat it as a no-op like zlib's own gzflush() does. */ + err = Z_OK; + } + if (flush != Z_FINISH && err == Z_BUF_ERROR && z->stream.avail_out > 0) { z->flags |= ZSTREAM_FLAG_IN_STREAM; } diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index 1f9cb81..9cfd09e 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -121,6 +121,16 @@ def test_flush assert_equal("foobarbazqux", Zlib::Inflate.inflate(s)) end + def test_flush_twice + z = Zlib::Deflate.new + s = z.deflate("foo", Zlib::SYNC_FLUSH) + s << z.flush << z.flush + s << z.flush(Zlib::FULL_FLUSH) << z.flush(Zlib::SYNC_FLUSH) + s << z.deflate("", Zlib::SYNC_FLUSH) + s << z.finish + assert_equal("foo", Zlib::Inflate.inflate(s)) + end + def test_avail z = Zlib::Deflate.new assert_equal(0, z.avail_in) @@ -1535,6 +1545,22 @@ def test_puts } end + def test_flush_twice + io = StringIO.new("".b) + Zlib::GzipWriter.wrap(io) do |gz| + gz.write("a") + gz.flush + gz.flush + gz.flush(Zlib::FULL_FLUSH) + gz.flush(Zlib::SYNC_FLUSH) + gz.sync = true + gz.write("") + gz.write("") + gz.write("b") + end + assert_equal("ab", Zlib.gunzip(io.string)) + end + def test_writer_wrap Tempfile.create("test_zlib_gzip_writer_wrap") {|t| t.binmode