というわけで、その次は Zlib::GzipReader#readpartial を次のように追加す
るのはどうでしょうか。

[ruby-dev:24066] のパッチを当てた後に当てるパッチです。

--- ext/zlib/zlib.c	2004-08-13 18:38:28.000000000 +0900
+++ ext/zlib/zlib.c-readpartial	2004-08-13 18:08:12.000000000 +0900
@@ -107,6 +107,8 @@
 static void gzfile_reset _((struct gzfile*));
 static void gzfile_close _((struct gzfile*, int));
 static void gzfile_write_raw _((struct gzfile*));
+static VALUE gzfile_read_raw_partial _((VALUE));
+static VALUE gzfile_read_raw_rescue _((VALUE));
 static VALUE gzfile_read_raw _((struct gzfile*));
 static int gzfile_read_raw_ensure _((struct gzfile*, int));
 static char *gzfile_read_raw_until_zero _((struct gzfile*, long));
@@ -1664,7 +1666,7 @@
 #define OS_CODE  OS_UNIX
 #endif
 
-static ID id_write, id_read, id_flush, id_seek, id_close;
+static ID id_write, id_read, id_readpartial, id_flush, id_seek, id_close;
 static VALUE cGzError, cNoFooter, cCRCError, cLengthError;
 
 
@@ -1787,18 +1789,41 @@
 }
 
 static VALUE
-gzfile_read_raw(gz)
-    struct gzfile *gz;
+gzfile_read_raw_partial(arg)
+    VALUE arg;
 {
+    struct gzfile *gz = (struct gzfile *)arg;
     VALUE str;
 
-    str = rb_funcall(gz->io, id_read, 1, INT2FIX(GZFILE_READ_SIZE));
-    if (!NIL_P(str)) {
-	Check_Type(str, T_STRING);
+    str = rb_funcall(gz->io, id_readpartial, 1, INT2FIX(GZFILE_READ_SIZE));
+    Check_Type(str, T_STRING);
+    return str;
+}
+
+static VALUE
+gzfile_read_raw_rescue(arg)
+    VALUE arg;
+{
+    struct gzfile *gz = (struct gzfile *)arg;
+    VALUE str = Qnil;
+    if (rb_obj_is_kind_of(ruby_errinfo, rb_eNoMethodError)) {
+        str = rb_funcall(gz->io, id_read, 1, INT2FIX(GZFILE_READ_SIZE));
+        if (!NIL_P(str)) {
+            Check_Type(str, T_STRING);
+        }
     }
     return str;
 }
 
+static VALUE
+gzfile_read_raw(gz)
+    struct gzfile *gz;
+{
+    return rb_rescue2(gzfile_read_raw_partial, (VALUE)gz,
+                      gzfile_read_raw_rescue, (VALUE)gz,
+                      rb_eEOFError, rb_eNoMethodError, (VALUE)0);
+}
+
 static int
 gzfile_read_raw_ensure(gz, size)
     struct gzfile *gz;
@@ -2113,6 +2138,54 @@
 }
 
 static VALUE
+gzfile_readpartial(gz, len, outbuf)
+    struct gzfile *gz;
+    int len;
+    VALUE outbuf;
+{
+    VALUE dst;
+
+    if (len < 0)
+        rb_raise(rb_eArgError, "negative length %d given", len);
+
+    if (!NIL_P(outbuf))
+            OBJ_TAINT(outbuf);
+
+    if (len == 0) {
+        if (NIL_P(outbuf))
+            return rb_str_new(0, 0);
+        else {
+            rb_str_resize(outbuf, 0);
+            return outbuf;
+        }
+    }
+    while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled == 0) {
+	gzfile_read_more(gz);
+    }
+    if (GZFILE_IS_FINISHED(gz)) {
+	if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
+	    gzfile_check_footer(gz);
+	}
+        if (!NIL_P(outbuf))
+            rb_str_resize(outbuf, 0);
+	rb_raise(rb_eEOFError, "End of file reached");
+    }
+
+    dst = zstream_shift_buffer(&gz->z, len);
+    gzfile_calc_crc(gz, dst);
+
+    if (NIL_P(outbuf)) {
+        OBJ_TAINT(dst);  /* for safe */
+        return dst;
+    }
+    else {
+        rb_str_resize(outbuf, RSTRING(dst)->len);
+        memcpy(RSTRING(outbuf)->ptr, RSTRING(dst)->ptr, RSTRING(dst)->len);
+        return outbuf;
+    }
+}
+
+static VALUE
 gzfile_read_all(gz)
     struct gzfile *gz;
 {
@@ -2940,6 +3013,30 @@
  * See Zlib::GzipReader documentation for a description.
  */
 static VALUE
+rb_gzreader_readpartial(argc, argv, obj)
+    int argc;
+    VALUE *argv;
+    VALUE obj;
+{
+    struct gzfile *gz = get_gzfile(obj);
+    VALUE vlen, outbuf;
+    int len;
+
+    rb_scan_args(argc, argv, "11", &vlen, &outbuf);
+
+    len = NUM2INT(vlen);
+    if (len < 0) {
+	rb_raise(rb_eArgError, "negative length %d given", len);
+    }
+    if (!NIL_P(outbuf))
+        Check_Type(outbuf, T_STRING);
+    return gzfile_readpartial(gz, len, outbuf);
+}
+
+/*
+ * See Zlib::GzipReader documentation for a description.
+ */
+static VALUE
 rb_gzreader_getc(obj)
     VALUE obj;
 {
@@ -3340,6 +3437,7 @@
 #if GZIP_SUPPORT
     id_write = rb_intern("write");
     id_read = rb_intern("read");
+    id_readpartial = rb_intern("readpartial");
     id_flush = rb_intern("flush");
     id_seek = rb_intern("seek");
     id_close = rb_intern("close");
@@ -3398,6 +3496,7 @@
     rb_define_method(cGzipReader, "rewind", rb_gzreader_rewind, 0);
     rb_define_method(cGzipReader, "unused", rb_gzreader_unused, 0);
     rb_define_method(cGzipReader, "read", rb_gzreader_read, -1);
+    rb_define_method(cGzipReader, "readpartial", rb_gzreader_readpartial, -1);
     rb_define_method(cGzipReader, "getc", rb_gzreader_getc, 0);
     rb_define_method(cGzipReader, "readchar", rb_gzreader_readchar, 0);
     rb_define_method(cGzipReader, "each_byte", rb_gzreader_each_byte, 0);

ちなみに、

% gzip -9 < eval.c | ./ruby -rzlib -e '
STDOUT.sync = true
r, w = IO.pipe
w.sync = true
ilen = 0
Thread.new {
  begin
    loop {
      w.print STDIN.sysread(1)
      ilen += 1
      Thread.pass
    }
  rescue EOFError
  end
}
io = Zlib::GzipReader.new(r)
begin
  loop { p [ilen, io.readpartial(4096)] }
rescue EOFError
end
'

などとやると、

[10, "/"]
[81, "*"]
[82, "*********************************************************************"]
[85, "\n"]
[86, "\n "]
[87, " "]
...

というように、辞書が成長していく感じが見えてなかなか楽しいです。
-- 
[田中 哲][たなか あきら][Tanaka Akira]