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:
parent
1f63c0fcb8
commit
583b6dc33c
3 changed files with 183 additions and 9 deletions
4
NEWS
4
NEWS
|
@ -343,6 +343,10 @@ with all sufficient information, see the ChangeLog file or Redmine
|
|||
* 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
|
||||
|
|
142
ext/zlib/zlib.c
142
ext/zlib/zlib.c
|
@ -2285,16 +2285,9 @@ static const rb_data_type_t gzfile_data_type = {
|
|||
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)
|
|||
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, long size)
|
|||
{
|
||||
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)
|
|||
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)
|
|||
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 */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding: us-ascii
|
||||
# frozen_string_literal: false
|
||||
require 'test/unit'
|
||||
require 'stringio'
|
||||
|
@ -1126,5 +1127,50 @@ if defined? Zlib
|
|||
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
|
||||
|
|
Loading…
Add table
Reference in a new issue