[前][次][番号順一覧][スレッド一覧]

ruby-changes:62936

From: Jeremy <ko1@a...>
Date: Mon, 14 Sep 2020 17:53:47 +0900 (JST)
Subject: [ruby-changes:62936] d52dffd817 (master): [ruby/zlib] Add Zlib::GzipReader.zcat for handling multiple gzip streams in gz file

https://git.ruby-lang.org/ruby.git/commit/?id=d52dffd817

From d52dffd817d9285f7600138e2f69f46891fff845 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Tue, 26 Nov 2019 17:31:47 -0800
Subject: [ruby/zlib] Add Zlib::GzipReader.zcat for handling multiple gzip
 streams in gz file

Most gzip tools support concatenated gz streams in a gz file. This
offers a way to handle such gz files in Ruby.

Fixes [Bug #9790]
Fixes [Bug #11180]
Fixes [Bug #14804]

https://github.com/ruby/zlib/commit/e2ce56de7d

diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 5c8aab2..bc41b55 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -3724,6 +3724,60 @@ rb_gzreader_s_open(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L3724
 }
 
 /*
+ * Document-method: Zlib::GzipReader.zcat
+ *
+ * call-seq:
+ *   Zlib::GzipReader.zcat(io, options = {}, &block) => nil
+ *   Zlib::GzipReader.zcat(io, options = {}) => string
+ *
+ * Decompresses all gzip data in the +io+, handling multiple gzip
+ * streams until the end of the +io+.  There should not be any non-gzip
+ * data after the gzip streams.
+ *
+ * If a block is given, it is yielded strings of uncompressed data,
+ * and the method returns +nil+.
+ * If a block is not given, the method returns the concatenation of
+ * all uncompressed data in all gzip streams.
+ */
+static VALUE
+rb_gzreader_s_zcat(int argc, VALUE *argv, VALUE klass)
+{
+    VALUE io, unused, obj, buf=0, tmpbuf;
+    long pos;
+
+    rb_check_arity(argc, 1, 2);
+    io = argv[0];
+
+    do {
+        obj = rb_funcallv(klass, rb_intern("new"), argc, argv);
+        if (rb_block_given_p()) {
+           rb_gzreader_each(0, 0, obj);
+        }
+        else {
+            if (!buf) {
+                buf = rb_str_new(0, 0);
+            }
+            tmpbuf = gzfile_read_all(get_gzfile(obj));
+            rb_str_cat(buf, RSTRING_PTR(tmpbuf), RSTRING_LEN(tmpbuf));
+        }
+
+        rb_gzreader_read(0, 0, obj);
+        pos = NUM2LONG(rb_funcall(io, rb_intern("pos"), 0));
+        unused = rb_gzreader_unused(obj);
+        rb_gzfile_finish(obj);
+        if (!NIL_P(unused)) {
+            pos -= NUM2LONG(rb_funcall(unused, rb_intern("length"), 0));
+            rb_funcall(io, rb_intern("pos="), 1, LONG2NUM(pos));
+        }
+    } while (pos < NUM2LONG(rb_funcall(io, rb_intern("size"), 0)));
+
+    if (rb_block_given_p()) {
+        return Qnil;
+    }
+    return buf;
+}
+
+/*
  * Document-method: Zlib::GzipReader.new
  *
  * call-seq:
@@ -4696,6 +4750,7 @@ Init_zlib(void) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L4750
     rb_define_method(cGzipWriter, "puts", rb_gzwriter_puts, -1);
 
     rb_define_singleton_method(cGzipReader, "open", rb_gzreader_s_open,-1);
+    rb_define_singleton_method(cGzipReader, "zcat", rb_gzreader_s_zcat, -1);
     rb_define_alloc_func(cGzipReader, rb_gzreader_s_allocate);
     rb_define_method(cGzipReader, "initialize", rb_gzreader_initialize, -1);
     rb_define_method(cGzipReader, "rewind", rb_gzreader_rewind, 0);
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index 7d703d1..c58eafe 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -446,6 +446,30 @@ if defined? Zlib https://github.com/ruby/ruby/blob/trunk/test/zlib/test_zlib.rb#L446
   end
 
   class TestZlibGzipFile < Test::Unit::TestCase
+    def test_gzip_reader_zcat
+      Tempfile.create("test_zlib_gzip_file_to_io") {|t|
+        gz = Zlib::GzipWriter.new(t)
+        gz.print("foo")
+        gz.close
+        t = File.open(t.path, 'ab')
+        gz = Zlib::GzipWriter.new(t)
+        gz.print("bar")
+        gz.close
+
+        results = []
+        t = File.open(t.path)
+        Zlib::GzipReader.zcat(t) do |str|
+          results << str
+        end
+        assert_equal(["foo", "bar"], results)
+        t.close
+
+        t = File.open(t.path)
+        assert_equal("foobar", Zlib::GzipReader.zcat(t))
+        t.close
+      }
+    end
+
     def test_to_io
       Tempfile.create("test_zlib_gzip_file_to_io") {|t|
         t.close
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]