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

ruby-changes:44962

From: naruse <ko1@a...>
Date: Sat, 10 Dec 2016 07:45:47 +0900 (JST)
Subject: [ruby-changes:44962] naruse:r57035 (trunk): Zlib.gzip and Zlib.gunzip [Feature #13020]

naruse	2016-12-10 07:45:39 +0900 (Sat, 10 Dec 2016)

  New Revision: 57035

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=57035

  Log:
    Zlib.gzip and Zlib.gunzip [Feature #13020]
    
    Encode and Decode gzip data without creating files.

  Modified files:
    trunk/NEWS
    trunk/ext/zlib/zlib.c
    trunk/test/zlib/test_zlib.rb
Index: test/zlib/test_zlib.rb
===================================================================
--- test/zlib/test_zlib.rb	(revision 57034)
+++ test/zlib/test_zlib.rb	(revision 57035)
@@ -1,3 +1,4 @@ https://github.com/ruby/ruby/blob/trunk/test/zlib/test_zlib.rb#L1
+# coding: us-ascii
 # frozen_string_literal: false
 require 'test/unit'
 require 'stringio'
@@ -1126,5 +1127,50 @@ if defined? Zlib https://github.com/ruby/ruby/blob/trunk/test/zlib/test_zlib.rb#L1127
       assert_equal 20016, deflated.length
     end
 
+    def test_gzip
+      actual = Zlib.gzip("foo")
+      actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+      actual[9] = "\xff" # replace OS
+      expected = %w[1f8b08000000000000ff4bcbcf07002165738c03000000].pack("H*")
+      assert_equal expected, actual
+
+      actual = Zlib.gzip("foo", 0)
+      actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+      actual[9] = "\xff" # replace OS
+      expected = %w[1f8b08000000000000ff010300fcff666f6f2165738c03000000].pack("H*")
+      assert_equal expected, actual
+
+      actual = Zlib.gzip("foo", 9)
+      actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+      actual[9] = "\xff" # replace OS
+      expected = %w[1f8b08000000000002ff4bcbcf07002165738c03000000].pack("H*")
+      assert_equal expected, actual
+
+      actual = Zlib.gzip("foo", 9, Zlib::FILTERED)
+      actual[4, 4] = "\x00\x00\x00\x00" # replace mtime
+      actual[9] = "\xff" # replace OS
+      expected = %w[1f8b08000000000002ff4bcbcf07002165738c03000000].pack("H*")
+      assert_equal expected, actual
+    end
+
+    def test_gunzip
+      src = %w[1f8b08000000000000034bcbcf07002165738c03000000].pack("H*")
+      assert_equal 'foo', Zlib.gunzip(src)
+
+      src = %w[1f8b08000000000000034bcbcf07002165738c03000001].pack("H*")
+      assert_raise(Zlib::GzipFile::LengthError){ Zlib.gunzip(src) }
+
+      src = %w[1f8b08000000000000034bcbcf07002165738d03000000].pack("H*")
+      assert_raise(Zlib::GzipFile::CRCError){ Zlib.gunzip(src) }
+
+      src = %w[1f8b08000000000000034bcbcf07002165738d030000].pack("H*")
+      assert_raise(Zlib::GzipFile::Error){ Zlib.gunzip(src) }
+
+      src = %w[1f8b08000000000000034bcbcf0700].pack("H*")
+      assert_raise(Zlib::GzipFile::NoFooter){ Zlib.gunzip(src) }
+
+      src = %w[1f8b080000000000000].pack("H*")
+      assert_raise(Zlib::GzipFile::Error){ Zlib.gunzip(src) }
+    end
   end
 end
Index: ext/zlib/zlib.c
===================================================================
--- ext/zlib/zlib.c	(revision 57034)
+++ ext/zlib/zlib.c	(revision 57035)
@@ -2285,16 +2285,9 @@ static const rb_data_type_t gzfile_data_ https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L2285
      0, 0, RUBY_TYPED_FREE_IMMEDIATELY
 };
 
-static VALUE
-gzfile_new(klass, funcs, endfunc)
-    VALUE klass;
-    const struct zstream_funcs *funcs;
-    void (*endfunc)(struct gzfile *);
+static void
+gzfile_init(struct gzfile *gz, const struct zstream_funcs *funcs, void (*endfunc)(struct gzfile *))
 {
-    VALUE obj;
-    struct gzfile *gz;
-
-    obj = TypedData_Make_Struct(klass, struct gzfile, &gzfile_data_type, gz);
     zstream_init(&gz->z, funcs);
     gz->z.flags |= ZSTREAM_FLAG_GZFILE;
     gz->io = Qnil;
@@ -2314,7 +2307,16 @@ gzfile_new(klass, funcs, endfunc) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L2307
     gz->ecopts = Qnil;
     gz->cbuf = 0;
     gz->path = Qnil;
+}
+
+static VALUE
+gzfile_new(VALUE klass, const struct zstream_funcs *funcs, void (*endfunc)(struct gzfile *))
+{
+    VALUE obj;
+    struct gzfile *gz;
 
+    obj = TypedData_Make_Struct(klass, struct gzfile, &gzfile_data_type, gz);
+    gzfile_init(gz, funcs, endfunc);
     return obj;
 }
 
@@ -2403,6 +2405,12 @@ gzfile_read_raw_ensure(struct gzfile *gz https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L2405
 {
     VALUE str;
 
+    if (gz->io == Qundef) { /* Zlib.gunzip */
+	if (NIL_P(gz->z.input))
+	    rb_bug("unexpected condition: both gz->io and gz->z.input are nil");
+	if (RSTRING_LEN(gz->z.input) < size)
+	    rb_raise(cGzError, "unexpected end of string");
+    }
     while (NIL_P(gz->z.input) || RSTRING_LEN(gz->z.input) < size) {
 	str = gzfile_read_raw(gz);
 	if (NIL_P(str)) return 0;
@@ -4262,6 +4270,119 @@ rb_gzreader_external_encoding(VALUE self https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L4270
     return rb_enc_from_encoding(get_gzfile(self)->enc);
 }
 
+static void
+zlib_gzip_end(struct gzfile *gz)
+{
+    gz->z.flags |= ZSTREAM_FLAG_CLOSING;
+    zstream_run(&gz->z, (Bytef*)"", 0, Z_FINISH);
+    gzfile_make_footer(gz);
+    zstream_end(&gz->z);
+}
+
+/*
+ * call-seq:
+ *   Zlib.gunzip(src) -> String
+ *
+ * Decode the given gzipped +string+.
+ *
+ * This method is almost equivalent to the following code:
+ *
+ *   def gunzip(string)
+ *     sio = StringIO.new(string)
+ *     Zlib::GzipReadr.new(sio){|f| f.read}
+ *   end
+ *
+ * See also Zlib.gzip
+ */
+static VALUE
+zlib_s_gzip(int argc, VALUE *argv, VALUE klass)
+{
+    struct gzfile gz0;
+    struct gzfile *gz = &gz0;
+    long len;
+    int err;
+    VALUE src, level, strategy;
+
+    rb_scan_args(argc, argv, "12", &src, &level, &strategy);
+    StringValue(src);
+    gzfile_init(gz, &deflate_funcs, zlib_gzip_end);
+    gz->level = ARG_LEVEL(level);
+    err = deflateInit2(&gz->z.stream, gz->level, Z_DEFLATED,
+		       -MAX_WBITS, DEF_MEM_LEVEL, ARG_STRATEGY(strategy));
+    if (err != Z_OK) {
+	raise_zlib_error(err, gz->z.stream.msg);
+    }
+    ZSTREAM_READY(&gz->z);
+    gzfile_make_header(gz);
+    len = RSTRING_LEN(src);
+    if (len > 0) {
+	Bytef *ptr = (Bytef *)RSTRING_PTR(src);
+	gz->crc = checksum_long(crc32, gz->crc, ptr, len);
+	zstream_run(&gz->z, ptr, len, Z_NO_FLUSH);
+    }
+    gzfile_close(gz, 0);
+    return zstream_detach_buffer(&gz->z);
+}
+
+static void
+zlib_gunzip_end(struct gzfile *gz)
+{
+    gz->z.flags |= ZSTREAM_FLAG_CLOSING;
+    gzfile_check_footer(gz);
+    zstream_end(&gz->z);
+}
+
+/*
+ * call-seq:
+ *   Zlib.gunzip(src, level=nil, strategy=nil) -> String
+ *
+ * Gzip the given +string+. Valid values of level are
+ * Zlib::NO_COMPRESSION, Zlib::BEST_SPEED, Zlib::BEST_COMPRESSION,
+ * Zlib::DEFAULT_COMPRESSION (default), or an integer from 0 to 9.
+ *
+ * This method is almost equivalent to the following code:
+ *
+ *   def gzip(string, level=nil, strategy=nil)
+ *     sio = StringIO.new
+ *     gz = Zlib::GzipWriter.new(sio, level, strategy)
+ *     gz.write(string)
+ *     gz.close
+ *     sio.string
+ *   end
+ *
+ * See also Zlib.gunzip
+ *
+ */
+static VALUE
+zlib_gunzip(VALUE klass, VALUE src)
+{
+    struct gzfile gz0;
+    struct gzfile *gz = &gz0;
+    int err;
+    VALUE dst;
+
+    StringValue(src);
+
+    gzfile_init(gz, &inflate_funcs, zlib_gunzip_end);
+    err = inflateInit2(&gz->z.stream, -MAX_WBITS);
+    if (err != Z_OK) {
+	raise_zlib_error(err, gz->z.stream.msg);
+    }
+    gz->io = Qundef;
+    gz->z.input = src;
+    ZSTREAM_READY(&gz->z);
+    gzfile_read_header(gz);
+    dst = zstream_detach_buffer(&gz->z);
+    gzfile_calc_crc(gz, dst);
+	    if (!ZSTREAM_IS_FINISHED(&gz->z)) {
+		rb_raise(cGzError, "unexpected end of file");
+	    }
+    if (NIL_P(gz->z.input))
+	rb_raise(cNoFooter, "footer is not found");
+    gzfile_check_footer(gz);
+    return dst;
+}
+
 #endif /* GZIP_SUPPORT */
 
 void
@@ -4532,6 +4653,9 @@ Init_zlib(void) https://github.com/ruby/ruby/blob/trunk/ext/zlib/zlib.c#L4653
     rb_define_method(cGzipReader, "readlines", rb_gzreader_readlines, -1);
     rb_define_method(cGzipReader, "external_encoding", rb_gzreader_external_encoding, 0);
 
+    rb_define_singleton_method(mZlib, "gzip", zlib_s_gzip, -1);
+    rb_define_singleton_method(mZlib, "gunzip", zlib_gunzip, 1);
+
     /* The OS code of current host */
     rb_define_const(mZlib, "OS_CODE", INT2FIX(OS_CODE));
     /* OS code for MSDOS hosts */
Index: NEWS
===================================================================
--- NEWS	(revision 57034)
+++ NEWS	(revision 57035)
@@ -343,6 +343,10 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L343
   * XMLRPC is removed from stdlib. [Feature #12160][ruby-core:74239]
     https://github.com/ruby/xmlrpc is the new upstream.
 
+* Zlib
+
+  * Zlib.gzip and Zlib.gunzip [Feature #13020]
+
 === C API updates
 
 * ruby_show_version() will no longer exits the process, if

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

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