1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Zlib.gzip and Zlib.gunzip [Feature #13020]

Encode and Decode gzip data without creating files.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57035 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
naruse 2016-12-09 22:45:39 +00:00
parent 1f63c0fcb8
commit 583b6dc33c
3 changed files with 183 additions and 9 deletions

4
NEWS
View file

@ -343,6 +343,10 @@ with all sufficient information, see the ChangeLog file or Redmine
* XMLRPC is removed from stdlib. [Feature #12160][ruby-core:74239] * XMLRPC is removed from stdlib. [Feature #12160][ruby-core:74239]
https://github.com/ruby/xmlrpc is the new upstream. https://github.com/ruby/xmlrpc is the new upstream.
* Zlib
* Zlib.gzip and Zlib.gunzip [Feature #13020]
=== C API updates === C API updates
* ruby_show_version() will no longer exits the process, if * ruby_show_version() will no longer exits the process, if

View file

@ -2285,16 +2285,9 @@ static const rb_data_type_t gzfile_data_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
}; };
static VALUE static void
gzfile_new(klass, funcs, endfunc) gzfile_init(struct gzfile *gz, const struct zstream_funcs *funcs, void (*endfunc)(struct gzfile *))
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);
zstream_init(&gz->z, funcs); zstream_init(&gz->z, funcs);
gz->z.flags |= ZSTREAM_FLAG_GZFILE; gz->z.flags |= ZSTREAM_FLAG_GZFILE;
gz->io = Qnil; gz->io = Qnil;
@ -2314,7 +2307,16 @@ gzfile_new(klass, funcs, endfunc)
gz->ecopts = Qnil; gz->ecopts = Qnil;
gz->cbuf = 0; gz->cbuf = 0;
gz->path = Qnil; 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; return obj;
} }
@ -2403,6 +2405,12 @@ gzfile_read_raw_ensure(struct gzfile *gz, long size)
{ {
VALUE str; 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) { while (NIL_P(gz->z.input) || RSTRING_LEN(gz->z.input) < size) {
str = gzfile_read_raw(gz); str = gzfile_read_raw(gz);
if (NIL_P(str)) return 0; if (NIL_P(str)) return 0;
@ -4262,6 +4270,119 @@ rb_gzreader_external_encoding(VALUE self)
return rb_enc_from_encoding(get_gzfile(self)->enc); 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 */ #endif /* GZIP_SUPPORT */
void void
@ -4532,6 +4653,9 @@ Init_zlib(void)
rb_define_method(cGzipReader, "readlines", rb_gzreader_readlines, -1); rb_define_method(cGzipReader, "readlines", rb_gzreader_readlines, -1);
rb_define_method(cGzipReader, "external_encoding", rb_gzreader_external_encoding, 0); 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 */ /* The OS code of current host */
rb_define_const(mZlib, "OS_CODE", INT2FIX(OS_CODE)); rb_define_const(mZlib, "OS_CODE", INT2FIX(OS_CODE));
/* OS code for MSDOS hosts */ /* OS code for MSDOS hosts */

View file

@ -1,3 +1,4 @@
# coding: us-ascii
# frozen_string_literal: false # frozen_string_literal: false
require 'test/unit' require 'test/unit'
require 'stringio' require 'stringio'
@ -1126,5 +1127,50 @@ if defined? Zlib
assert_equal 20016, deflated.length assert_equal 20016, deflated.length
end 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
end end