Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix eof? to return true when there is no more data to read #73

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions ext/zlib/zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -3500,6 +3500,14 @@ static VALUE
rb_gzfile_eof_p(VALUE obj)
{
struct gzfile *gz = get_gzfile(obj);

// Similar to Socket/Pipe, we need to check if there is any actual data left,
// as it is possible that the stream is not finished, but there is no data that
// would be returned upon reading, so we want to report eof? in that case
while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) == 0) {
gzfile_read_more(gz, Qnil);
}

return GZFILE_IS_FINISHED(gz) ? Qtrue : Qfalse;
}

Expand Down Expand Up @@ -4830,6 +4838,9 @@ Init_zlib(void)
rb_define_attr(cGzError, "input", 1, 0);
rb_define_method(cGzError, "inspect", gzfile_error_inspect, 0);

/* The block size in bytes that the gzip file reads from its underlying io */
rb_define_const(cGzipFile, "READ_SIZE", INT2FIX(GZFILE_READ_SIZE));

cNoFooter = rb_define_class_under(cGzipFile, "NoFooter", cGzError);
cCRCError = rb_define_class_under(cGzipFile, "CRCError", cGzError);
cLengthError = rb_define_class_under(cGzipFile,"LengthError",cGzError);
Expand Down
62 changes: 62 additions & 0 deletions test/zlib/test_zlib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,68 @@ def test_double_close
}
end

# Test boundary cases around compressed data where the "final" deflate block is empty,
# and close to a Zlib::GzipFile::READ_SIZE boundary
[(Zlib::GzipFile::READ_SIZE - 30)..(Zlib::GzipFile::READ_SIZE - 10), (Zlib::GzipFile::READ_SIZE * 2 - 30)..(Zlib::GzipFile::READ_SIZE * 2 - 10)].flat_map(&:to_a).each do |size|
contents = "a\n" * ((size + 1) / 2)
contents.freeze

s = StringIO.new
gz = Zlib::GzipWriter.new(s, Zlib::NO_COMPRESSION)
gz.write contents
gz.flush
gz.close
s = s.string.freeze

sub_test_case("String of length #{size}") do
[[:readpartial, 500], [:readpartial, 1024], [:readpartial, 2028], [:gets], [:getc], [:getbyte], [:read], [:read, 500], [:read, 1024], [:read, 2048], [:readline], [:readlines], [:readbyte], [:readchar]].each do |method, *args|
test("#{method}(#{args.join(', ')})") do
read = "".b
Zlib::GzipReader.wrap(StringIO.new(s)) do |gzio|
until gzio.eof?
part = gzio.send(method, *args)
refute_nil part
case part
when Array then part.each { |e| read << e }
else read << part
end
end
assert_predicate gzio, :eof?

case method
when :readbyte, :getbyte
b = read.bytes.last
gzio.ungetbyte b
refute_predicate gzio, :eof?
assert_equal b, gzio.send(method, *args)
assert_predicate gzio, :eof?
when :readchar, :getc
c = read[-1]
gzio.ungetc c
refute_predicate gzio, :eof?
assert_equal c, gzio.send(method, *args)
assert_predicate gzio, :eof?
end

2.times do
case method
when :readpartial, :readbyte, :readchar, :readline
assert_raise(EOFError) { gzio.send(method, *args) }
when :readlines
assert_equal [], gzio.send(method, *args)
when ->(m) { m == :read && args.empty? }
assert_equal "", gzio.send(method, *args)
else
assert_nil gzio.send(method, *args)
end
assert_predicate gzio, :eof?
end
end
assert_equal contents, read
end
end
end
end
end

class TestZlibGzipWriter < Test::Unit::TestCase
Expand Down