ext/zlib/zlib.c in zlib-1.1.0 vs ext/zlib/zlib.c in zlib-2.0.0
- old
+ new
@@ -23,11 +23,11 @@
#else
# define VALGRIND_MAKE_MEM_DEFINED(p, n) 0
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
-#define RUBY_ZLIB_VERSION "1.1.0"
+#define RUBY_ZLIB_VERSION "2.0.0"
#ifndef RB_PASS_CALLED_KEYWORDS
# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
#endif
@@ -54,12 +54,15 @@
#define MAX_UINT(n) max_uint(n)
#else
#define MAX_UINT(n) (uInt)(n)
#endif
-static ID id_dictionaries;
+#define OPTHASH_GIVEN_P(opts) \
+ (argc > 0 && !NIL_P((opts) = rb_check_hash_type(argv[argc-1])) && (--argc, 1))
+static ID id_dictionaries, id_read, id_buffer;
+
/*--------- Prototypes --------*/
static NORETURN(void raise_zlib_error(int, const char*));
static VALUE rb_zlib_version(VALUE);
static VALUE do_checksum(int, VALUE*, uLong (*)(uLong, const Bytef*, uInt));
@@ -128,11 +131,11 @@
static VALUE inflate_run(VALUE);
static VALUE rb_inflate_s_allocate(VALUE);
static VALUE rb_inflate_initialize(int, VALUE*, VALUE);
static VALUE rb_inflate_s_inflate(VALUE, VALUE);
static void do_inflate(struct zstream*, VALUE);
-static VALUE rb_inflate_inflate(VALUE, VALUE);
+static VALUE rb_inflate_inflate(int, VALUE*, VALUE);
static VALUE rb_inflate_addstr(VALUE, VALUE);
static VALUE rb_inflate_sync(VALUE, VALUE);
static VALUE rb_inflate_sync_point_p(VALUE);
static VALUE rb_inflate_set_dictionary(VALUE, VALUE);
@@ -405,10 +408,19 @@
}
if (NIL_P(str)) {
sum = func(sum, Z_NULL, 0);
}
+ else if (rb_obj_is_kind_of(str, rb_cIO)) {
+ VALUE buf;
+ VALUE buflen = INT2NUM(8192);
+
+ while (!NIL_P(buf = rb_funcall(str, id_read, 1, buflen))) {
+ StringValue(buf);
+ sum = checksum_long(func, sum, (Bytef*)RSTRING_PTR(buf), RSTRING_LEN(buf));
+ }
+ }
else {
StringValue(str);
sum = checksum_long(func, sum, (Bytef*)RSTRING_PTR(str), RSTRING_LEN(str));
}
return rb_uint2inum(sum);
@@ -420,10 +432,12 @@
* call-seq: Zlib.adler32(string, adler)
*
* Calculates Adler-32 checksum for +string+, and returns updated value of
* +adler+. If +string+ is omitted, it returns the Adler-32 initial value. If
* +adler+ is omitted, it assumes that the initial value is given to +adler+.
+ * If +string+ is an IO instance, reads from the IO until the IO returns nil
+ * and returns Adler-32 of all read data.
*
* Example usage:
*
* require "zlib"
*
@@ -464,11 +478,13 @@
*
* call-seq: Zlib.crc32(string, crc)
*
* Calculates CRC checksum for +string+, and returns updated value of +crc+. If
* +string+ is omitted, it returns the CRC initial value. If +crc+ is omitted, it
- * assumes that the initial value is given to +crc+.
+ * assumes that the initial value is given to +crc+. If +string+ is an IO instance,
+ * reads from the IO until the IO returns nil and returns CRC checksum of all read
+ * data.
*
* FIXME: expression.
*/
static VALUE
rb_zlib_crc32(int argc, VALUE *argv, VALUE klass)
@@ -542,19 +558,22 @@
#define ZSTREAM_FLAG_IN_STREAM 0x2
#define ZSTREAM_FLAG_FINISHED 0x4
#define ZSTREAM_FLAG_CLOSING 0x8
#define ZSTREAM_FLAG_GZFILE 0x10 /* disallows yield from expand_buffer for
gzip*/
-#define ZSTREAM_FLAG_UNUSED 0x20
+#define ZSTREAM_REUSE_BUFFER 0x20
+#define ZSTREAM_FLAG_UNUSED 0x40
#define ZSTREAM_READY(z) ((z)->flags |= ZSTREAM_FLAG_READY)
#define ZSTREAM_IS_READY(z) ((z)->flags & ZSTREAM_FLAG_READY)
#define ZSTREAM_IS_FINISHED(z) ((z)->flags & ZSTREAM_FLAG_FINISHED)
#define ZSTREAM_IS_CLOSING(z) ((z)->flags & ZSTREAM_FLAG_CLOSING)
#define ZSTREAM_IS_GZFILE(z) ((z)->flags & ZSTREAM_FLAG_GZFILE)
#define ZSTREAM_BUF_FILLED(z) (NIL_P((z)->buf) ? 0 : RSTRING_LEN((z)->buf))
+#define ZSTREAM_REUSE_BUFFER_P(z) ((z)->flags & ZSTREAM_REUSE_BUFFER)
+
#define ZSTREAM_EXPAND_BUFFER_OK 0
/* I think that more better value should be found,
but I gave up finding it. B) */
#define ZSTREAM_INITIAL_BUFSIZE 1024
@@ -627,15 +646,23 @@
if (!ZSTREAM_IS_GZFILE(z) && rb_block_given_p()) {
long buf_filled = ZSTREAM_BUF_FILLED(z);
if (buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
int state = 0;
- rb_obj_reveal(z->buf, rb_cString);
+ if (!ZSTREAM_REUSE_BUFFER_P(z)) {
+ rb_obj_reveal(z->buf, rb_cString);
+ }
rb_protect(rb_yield, z->buf, &state);
- z->buf = Qnil;
+ if (ZSTREAM_REUSE_BUFFER_P(z)) {
+ rb_str_modify(z->buf);
+ rb_str_set_len(z->buf, 0);
+ }
+ else {
+ z->buf = Qnil;
+ }
zstream_expand_buffer_into(z, ZSTREAM_AVAIL_OUT_STEP_MAX);
if (state)
rb_jump_tag(state);
@@ -749,11 +776,13 @@
if (NIL_P(z->buf)) {
dst = rb_str_new(0, 0);
}
else {
dst = z->buf;
- rb_obj_reveal(dst, rb_cString);
+ if (!ZSTREAM_REUSE_BUFFER_P(z)) {
+ rb_obj_reveal(dst, rb_cString);
+ }
}
z->buf = Qnil;
z->stream.next_out = 0;
z->stream.avail_out = 0;
@@ -1998,21 +2027,30 @@
/*
* Document-method: Zlib::Inflate#inflate
*
* call-seq:
- * inflate(deflate_string) -> String
- * inflate(deflate_string) { |chunk| ... } -> nil
+ * inflate(deflate_string, buffer: nil) -> String
+ * inflate(deflate_string, buffer: nil) { |chunk| ... } -> nil
*
* Inputs +deflate_string+ into the inflate stream and returns the output from
* the stream. Calling this method, both the input and the output buffer of
* the stream are flushed. If string is +nil+, this method finishes the
* stream, just like Zlib::ZStream#finish.
*
* If a block is given consecutive inflated chunks from the +deflate_string+
* are yielded to the block and +nil+ is returned.
*
+ * If a :buffer keyword argument is given and not nil:
+ *
+ * * The :buffer keyword should be a String, and will used as the output buffer.
+ * Using this option can reuse the memory required during inflation.
+ * * When not passing a block, the return value will be the same object as the
+ * :buffer keyword argument.
+ * * When passing a block, the yielded chunks will be the same value as the
+ * :buffer keyword argument.
+ *
* Raises a Zlib::NeedDict exception if a preset dictionary is needed to
* decompress. Set the dictionary by Zlib::Inflate#set_dictionary and then
* call this method again with an empty string to flush the stream:
*
* inflater = Zlib::Inflate.new
@@ -2032,23 +2070,54 @@
* inflater.close
*
* See also Zlib::Inflate.new
*/
static VALUE
-rb_inflate_inflate(VALUE obj, VALUE src)
+rb_inflate_inflate(int argc, VALUE* argv, VALUE obj)
{
struct zstream *z = get_zstream(obj);
- VALUE dst;
+ VALUE dst, src, opts, buffer = Qnil;
+ if (OPTHASH_GIVEN_P(opts)) {
+ VALUE buf;
+ rb_get_kwargs(opts, &id_buffer, 0, 1, &buf);
+ if (buf != Qundef && buf != Qnil) {
+ buffer = StringValue(buf);
+ }
+ }
+ if (buffer != Qnil) {
+ if (!(ZSTREAM_REUSE_BUFFER_P(z) && z->buf == buffer)) {
+ long len = RSTRING_LEN(buffer);
+ if (len >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
+ rb_str_modify(buffer);
+ }
+ else {
+ len = ZSTREAM_AVAIL_OUT_STEP_MAX - len;
+ rb_str_modify_expand(buffer, len);
+ }
+ rb_str_set_len(buffer, 0);
+ z->flags |= ZSTREAM_REUSE_BUFFER;
+ z->buf = buffer;
+ }
+ } else if (ZSTREAM_REUSE_BUFFER_P(z)) {
+ z->flags &= ~ZSTREAM_REUSE_BUFFER;
+ z->buf = Qnil;
+ }
+ rb_scan_args(argc, argv, "10", &src);
+
if (ZSTREAM_IS_FINISHED(z)) {
if (NIL_P(src)) {
dst = zstream_detach_buffer(z);
}
else {
StringValue(src);
zstream_append_buffer2(z, src);
- dst = rb_str_new(0, 0);
+ if (ZSTREAM_REUSE_BUFFER_P(z)) {
+ dst = rb_str_resize(buffer, 0);
+ } else {
+ dst = rb_str_new(0, 0);
+ }
}
}
else {
do_inflate(z, src);
dst = zstream_detach_buffer(z);
@@ -2196,11 +2265,11 @@
#ifndef OS_CODE
#define OS_CODE OS_UNIX
#endif
-static ID id_write, id_read, id_readpartial, id_flush, id_seek, id_close, id_path, id_input;
+static ID id_write, id_readpartial, id_flush, id_seek, id_close, id_path, id_input;
static VALUE cGzError, cNoFooter, cCRCError, cLengthError;
/*-------- gzfile internal APIs --------*/
@@ -3722,10 +3791,64 @@
{
return gzfile_s_open(argc, argv, klass, "rb");
}
/*
+ * 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:
* Zlib::GzipReader.new(io, options = {})
*
@@ -3948,24 +4071,10 @@
}
return Qnil;
}
/*
- * Document-method: Zlib::GzipReader#bytes
- *
- * This is a deprecated alias for <code>each_byte</code>.
- */
-static VALUE
-rb_gzreader_bytes(VALUE obj)
-{
- rb_warn("Zlib::GzipReader#bytes is deprecated; use #each_byte instead");
- if (!rb_block_given_p())
- return rb_enumeratorize(obj, ID2SYM(rb_intern("each_byte")), 0, 0);
- return rb_gzreader_each_byte(obj);
-}
-
-/*
* Document-method: Zlib::GzipReader#ungetc
*
* See Zlib::GzipReader documentation for a description.
*/
static VALUE
@@ -4187,10 +4296,12 @@
/*
* Document-method: Zlib::GzipReader#gets
*
* See Zlib::GzipReader documentation for a description.
+ * However, note that this method can return +nil+ even if
+ * #eof? returns false, unlike the behavior of File#gets.
*/
static VALUE
rb_gzreader_gets(int argc, VALUE *argv, VALUE obj)
{
VALUE dst;
@@ -4234,24 +4345,10 @@
}
return obj;
}
/*
- * Document-method: Zlib::GzipReader#lines
- *
- * This is a deprecated alias for <code>each_line</code>.
- */
-static VALUE
-rb_gzreader_lines(int argc, VALUE *argv, VALUE obj)
-{
- rb_warn("Zlib::GzipReader#lines is deprecated; use #each_line instead");
- if (!rb_block_given_p())
- return rb_enumeratorize(obj, ID2SYM(rb_intern("each_line")), argc, argv);
- return rb_gzreader_each(argc, argv, obj);
-}
-
-/*
* Document-method: Zlib::GzipReader#readlines
*
* See Zlib::GzipReader documentation for a description.
*/
static VALUE
@@ -4297,12 +4394,10 @@
zstream_run(&gz->z, (Bytef*)"", 0, Z_FINISH);
gzfile_make_footer(gz);
zstream_end(&gz->z);
}
-#define OPTHASH_GIVEN_P(opts) \
- (argc > 0 && !NIL_P((opts) = rb_check_hash_type(argv[argc-1])) && (--argc, 1))
static ID id_level, id_strategy;
static VALUE zlib_gzip_run(VALUE arg);
/*
* call-seq:
@@ -4451,10 +4546,14 @@
#endif /* GZIP_SUPPORT */
void
Init_zlib(void)
{
+#if HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
+
#undef rb_intern
VALUE mZlib, cZStream, cDeflate, cInflate;
#if GZIP_SUPPORT
VALUE cGzipFile, cGzipWriter, cGzipReader;
#endif
@@ -4545,11 +4644,11 @@
rb_define_singleton_method(cInflate, "inflate", rb_inflate_s_inflate, 1);
rb_define_singleton_method(mZlib, "inflate", rb_inflate_s_inflate, 1);
rb_define_alloc_func(cInflate, rb_inflate_s_allocate);
rb_define_method(cInflate, "initialize", rb_inflate_initialize, -1);
rb_define_method(cInflate, "add_dictionary", rb_inflate_add_dictionary, 1);
- rb_define_method(cInflate, "inflate", rb_inflate_inflate, 1);
+ rb_define_method(cInflate, "inflate", rb_inflate_inflate, -1);
rb_define_method(cInflate, "<<", rb_inflate_addstr, 1);
rb_define_method(cInflate, "sync", rb_inflate_sync, 1);
rb_define_method(cInflate, "sync_point?", rb_inflate_sync_point_p, 0);
rb_define_method(cInflate, "set_dictionary", rb_inflate_set_dictionary, 1);
@@ -4694,10 +4793,11 @@
rb_define_method(cGzipWriter, "printf", rb_gzwriter_printf, -1);
rb_define_method(cGzipWriter, "print", rb_gzwriter_print, -1);
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);
rb_define_method(cGzipReader, "unused", rb_gzreader_unused, 0);
rb_define_method(cGzipReader, "read", rb_gzreader_read, -1);
@@ -4706,18 +4806,16 @@
rb_define_method(cGzipReader, "getbyte", rb_gzreader_getbyte, 0);
rb_define_method(cGzipReader, "readchar", rb_gzreader_readchar, 0);
rb_define_method(cGzipReader, "readbyte", rb_gzreader_readbyte, 0);
rb_define_method(cGzipReader, "each_byte", rb_gzreader_each_byte, 0);
rb_define_method(cGzipReader, "each_char", rb_gzreader_each_char, 0);
- rb_define_method(cGzipReader, "bytes", rb_gzreader_bytes, 0);
rb_define_method(cGzipReader, "ungetc", rb_gzreader_ungetc, 1);
rb_define_method(cGzipReader, "ungetbyte", rb_gzreader_ungetbyte, 1);
rb_define_method(cGzipReader, "gets", rb_gzreader_gets, -1);
rb_define_method(cGzipReader, "readline", rb_gzreader_readline, -1);
rb_define_method(cGzipReader, "each", rb_gzreader_each, -1);
rb_define_method(cGzipReader, "each_line", rb_gzreader_each, -1);
- rb_define_method(cGzipReader, "lines", rb_gzreader_lines, -1);
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);
@@ -4755,9 +4853,10 @@
/* OS code for unknown hosts */
rb_define_const(mZlib, "OS_UNKNOWN", INT2FIX(OS_UNKNOWN));
id_level = rb_intern("level");
id_strategy = rb_intern("strategy");
+ id_buffer = rb_intern("buffer");
#endif /* GZIP_SUPPORT */
}
/* Document error classes. */