diff options
Diffstat (limited to 'ext/zlib')
| -rw-r--r-- | ext/zlib/.gitignore | 1 | ||||
| -rw-r--r-- | ext/zlib/depend | 162 | ||||
| -rw-r--r-- | ext/zlib/extconf.rb | 96 | ||||
| -rw-r--r-- | ext/zlib/zlib.c | 1340 | ||||
| -rw-r--r-- | ext/zlib/zlib.gemspec | 31 |
5 files changed, 1186 insertions, 444 deletions
diff --git a/ext/zlib/.gitignore b/ext/zlib/.gitignore new file mode 100644 index 0000000000..069491b4b4 --- /dev/null +++ b/ext/zlib/.gitignore @@ -0,0 +1 @@ +/zlib-[1-9]*.*.* diff --git a/ext/zlib/depend b/ext/zlib/depend index f094d343dc..22e9ca867a 100644 --- a/ext/zlib/depend +++ b/ext/zlib/depend @@ -1,17 +1,177 @@ # AUTOGENERATED DEPENDENCIES START zlib.o: $(RUBY_EXTCONF_H) zlib.o: $(arch_hdrdir)/ruby/config.h +zlib.o: $(hdrdir)/ruby.h +zlib.o: $(hdrdir)/ruby/assert.h zlib.o: $(hdrdir)/ruby/backward.h +zlib.o: $(hdrdir)/ruby/backward/2/assume.h +zlib.o: $(hdrdir)/ruby/backward/2/attributes.h +zlib.o: $(hdrdir)/ruby/backward/2/bool.h +zlib.o: $(hdrdir)/ruby/backward/2/inttypes.h +zlib.o: $(hdrdir)/ruby/backward/2/limits.h +zlib.o: $(hdrdir)/ruby/backward/2/long_long.h +zlib.o: $(hdrdir)/ruby/backward/2/stdalign.h +zlib.o: $(hdrdir)/ruby/backward/2/stdarg.h zlib.o: $(hdrdir)/ruby/defines.h zlib.o: $(hdrdir)/ruby/encoding.h zlib.o: $(hdrdir)/ruby/intern.h +zlib.o: $(hdrdir)/ruby/internal/abi.h +zlib.o: $(hdrdir)/ruby/internal/anyargs.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/char.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/double.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/int.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/long.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/short.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +zlib.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +zlib.o: $(hdrdir)/ruby/internal/assume.h +zlib.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +zlib.o: $(hdrdir)/ruby/internal/attr/artificial.h +zlib.o: $(hdrdir)/ruby/internal/attr/cold.h +zlib.o: $(hdrdir)/ruby/internal/attr/const.h +zlib.o: $(hdrdir)/ruby/internal/attr/constexpr.h +zlib.o: $(hdrdir)/ruby/internal/attr/deprecated.h +zlib.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +zlib.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +zlib.o: $(hdrdir)/ruby/internal/attr/error.h +zlib.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +zlib.o: $(hdrdir)/ruby/internal/attr/forceinline.h +zlib.o: $(hdrdir)/ruby/internal/attr/format.h +zlib.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +zlib.o: $(hdrdir)/ruby/internal/attr/noalias.h +zlib.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +zlib.o: $(hdrdir)/ruby/internal/attr/noexcept.h +zlib.o: $(hdrdir)/ruby/internal/attr/noinline.h +zlib.o: $(hdrdir)/ruby/internal/attr/nonnull.h +zlib.o: $(hdrdir)/ruby/internal/attr/noreturn.h +zlib.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +zlib.o: $(hdrdir)/ruby/internal/attr/pure.h +zlib.o: $(hdrdir)/ruby/internal/attr/restrict.h +zlib.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +zlib.o: $(hdrdir)/ruby/internal/attr/warning.h +zlib.o: $(hdrdir)/ruby/internal/attr/weakref.h +zlib.o: $(hdrdir)/ruby/internal/cast.h +zlib.o: $(hdrdir)/ruby/internal/compiler_is.h +zlib.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +zlib.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +zlib.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +zlib.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +zlib.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +zlib.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +zlib.o: $(hdrdir)/ruby/internal/compiler_since.h +zlib.o: $(hdrdir)/ruby/internal/config.h +zlib.o: $(hdrdir)/ruby/internal/constant_p.h +zlib.o: $(hdrdir)/ruby/internal/core.h +zlib.o: $(hdrdir)/ruby/internal/core/rarray.h +zlib.o: $(hdrdir)/ruby/internal/core/rbasic.h +zlib.o: $(hdrdir)/ruby/internal/core/rbignum.h +zlib.o: $(hdrdir)/ruby/internal/core/rclass.h +zlib.o: $(hdrdir)/ruby/internal/core/rdata.h +zlib.o: $(hdrdir)/ruby/internal/core/rfile.h +zlib.o: $(hdrdir)/ruby/internal/core/rhash.h +zlib.o: $(hdrdir)/ruby/internal/core/robject.h +zlib.o: $(hdrdir)/ruby/internal/core/rregexp.h +zlib.o: $(hdrdir)/ruby/internal/core/rstring.h +zlib.o: $(hdrdir)/ruby/internal/core/rstruct.h +zlib.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +zlib.o: $(hdrdir)/ruby/internal/ctype.h +zlib.o: $(hdrdir)/ruby/internal/dllexport.h +zlib.o: $(hdrdir)/ruby/internal/dosish.h +zlib.o: $(hdrdir)/ruby/internal/encoding/coderange.h +zlib.o: $(hdrdir)/ruby/internal/encoding/ctype.h +zlib.o: $(hdrdir)/ruby/internal/encoding/encoding.h +zlib.o: $(hdrdir)/ruby/internal/encoding/pathname.h +zlib.o: $(hdrdir)/ruby/internal/encoding/re.h +zlib.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +zlib.o: $(hdrdir)/ruby/internal/encoding/string.h +zlib.o: $(hdrdir)/ruby/internal/encoding/symbol.h +zlib.o: $(hdrdir)/ruby/internal/encoding/transcode.h +zlib.o: $(hdrdir)/ruby/internal/error.h +zlib.o: $(hdrdir)/ruby/internal/eval.h +zlib.o: $(hdrdir)/ruby/internal/event.h +zlib.o: $(hdrdir)/ruby/internal/fl_type.h +zlib.o: $(hdrdir)/ruby/internal/gc.h +zlib.o: $(hdrdir)/ruby/internal/glob.h +zlib.o: $(hdrdir)/ruby/internal/globals.h +zlib.o: $(hdrdir)/ruby/internal/has/attribute.h +zlib.o: $(hdrdir)/ruby/internal/has/builtin.h +zlib.o: $(hdrdir)/ruby/internal/has/c_attribute.h +zlib.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +zlib.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +zlib.o: $(hdrdir)/ruby/internal/has/extension.h +zlib.o: $(hdrdir)/ruby/internal/has/feature.h +zlib.o: $(hdrdir)/ruby/internal/has/warning.h +zlib.o: $(hdrdir)/ruby/internal/intern/array.h +zlib.o: $(hdrdir)/ruby/internal/intern/bignum.h +zlib.o: $(hdrdir)/ruby/internal/intern/class.h +zlib.o: $(hdrdir)/ruby/internal/intern/compar.h +zlib.o: $(hdrdir)/ruby/internal/intern/complex.h +zlib.o: $(hdrdir)/ruby/internal/intern/cont.h +zlib.o: $(hdrdir)/ruby/internal/intern/dir.h +zlib.o: $(hdrdir)/ruby/internal/intern/enum.h +zlib.o: $(hdrdir)/ruby/internal/intern/enumerator.h +zlib.o: $(hdrdir)/ruby/internal/intern/error.h +zlib.o: $(hdrdir)/ruby/internal/intern/eval.h +zlib.o: $(hdrdir)/ruby/internal/intern/file.h +zlib.o: $(hdrdir)/ruby/internal/intern/hash.h +zlib.o: $(hdrdir)/ruby/internal/intern/io.h +zlib.o: $(hdrdir)/ruby/internal/intern/load.h +zlib.o: $(hdrdir)/ruby/internal/intern/marshal.h +zlib.o: $(hdrdir)/ruby/internal/intern/numeric.h +zlib.o: $(hdrdir)/ruby/internal/intern/object.h +zlib.o: $(hdrdir)/ruby/internal/intern/parse.h +zlib.o: $(hdrdir)/ruby/internal/intern/proc.h +zlib.o: $(hdrdir)/ruby/internal/intern/process.h +zlib.o: $(hdrdir)/ruby/internal/intern/random.h +zlib.o: $(hdrdir)/ruby/internal/intern/range.h +zlib.o: $(hdrdir)/ruby/internal/intern/rational.h +zlib.o: $(hdrdir)/ruby/internal/intern/re.h +zlib.o: $(hdrdir)/ruby/internal/intern/ruby.h +zlib.o: $(hdrdir)/ruby/internal/intern/select.h +zlib.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +zlib.o: $(hdrdir)/ruby/internal/intern/set.h +zlib.o: $(hdrdir)/ruby/internal/intern/signal.h +zlib.o: $(hdrdir)/ruby/internal/intern/sprintf.h +zlib.o: $(hdrdir)/ruby/internal/intern/string.h +zlib.o: $(hdrdir)/ruby/internal/intern/struct.h +zlib.o: $(hdrdir)/ruby/internal/intern/thread.h +zlib.o: $(hdrdir)/ruby/internal/intern/time.h +zlib.o: $(hdrdir)/ruby/internal/intern/variable.h +zlib.o: $(hdrdir)/ruby/internal/intern/vm.h +zlib.o: $(hdrdir)/ruby/internal/interpreter.h +zlib.o: $(hdrdir)/ruby/internal/iterator.h +zlib.o: $(hdrdir)/ruby/internal/memory.h +zlib.o: $(hdrdir)/ruby/internal/method.h +zlib.o: $(hdrdir)/ruby/internal/module.h +zlib.o: $(hdrdir)/ruby/internal/newobj.h +zlib.o: $(hdrdir)/ruby/internal/scan_args.h +zlib.o: $(hdrdir)/ruby/internal/special_consts.h +zlib.o: $(hdrdir)/ruby/internal/static_assert.h +zlib.o: $(hdrdir)/ruby/internal/stdalign.h +zlib.o: $(hdrdir)/ruby/internal/stdbool.h +zlib.o: $(hdrdir)/ruby/internal/stdckdint.h +zlib.o: $(hdrdir)/ruby/internal/symbol.h +zlib.o: $(hdrdir)/ruby/internal/value.h +zlib.o: $(hdrdir)/ruby/internal/value_type.h +zlib.o: $(hdrdir)/ruby/internal/variable.h +zlib.o: $(hdrdir)/ruby/internal/warning_push.h +zlib.o: $(hdrdir)/ruby/internal/xmalloc.h zlib.o: $(hdrdir)/ruby/io.h zlib.o: $(hdrdir)/ruby/missing.h +zlib.o: $(hdrdir)/ruby/onigmo.h zlib.o: $(hdrdir)/ruby/oniguruma.h zlib.o: $(hdrdir)/ruby/ruby.h zlib.o: $(hdrdir)/ruby/st.h zlib.o: $(hdrdir)/ruby/subst.h zlib.o: $(hdrdir)/ruby/thread.h -zlib.o: $(top_srcdir)/include/ruby.h zlib.o: zlib.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/zlib/extconf.rb b/ext/zlib/extconf.rb index 8bbede63b2..2b2dbb1a5b 100644 --- a/ext/zlib/extconf.rb +++ b/ext/zlib/extconf.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: false +# frozen_string_literal: true # # extconf.rb # @@ -10,10 +10,73 @@ require 'rbconfig' dir_config 'zlib' +libs = $libs +have_zlib = %w'z libz zlib1 zlib zdll zlibwapi'.any? {|z| have_library(z, 'deflateReset(NULL)', 'zlib.h')} -if %w'z libz zlib1 zlib zdll zlibwapi'.find {|z| have_library(z, 'deflateReset')} and - have_header('zlib.h') then +unless have_zlib + $libs = libs + unless File.directory?(zsrc = "#{$srcdir}/zlib") + dirs = Dir.open($srcdir) {|z| z.grep(/\Azlib-\d+[.\d]*\z/) {|x|"#{$srcdir}/#{x}"}} + dirs.delete_if {|x| !File.directory?(x)} + zsrc = dirs.max_by {|x| x.scan(/\d+/).map(&:to_i)} + end + if zsrc + addconf = [ + "ZSRC = $(srcdir)/#{File.basename(zsrc)}\n", + "all:\n", + ] + $INCFLAGS << " -I$(ZSRC)" + if $mswin or $mingw + dll = "zlib1.dll" + $extso << dll + $cleanfiles << "$(topdir)/#{dll}" << "$(ZIMPLIB)" + zmk = "\t$(MAKE) -f $(ZMKFILE) TOP=$(ZSRC)" + zopts = [] + if $nmake + zmkfile = "$(ZSRC)/win32/Makefile.msc" + m = "#{zsrc}/win32/Makefile.msc" + # zopts << "USE_ASM=1" + zopts << "ARCH=#{RbConfig::CONFIG['target_cpu']}" + else + zmkfile = "$(ZSRC)/win32/Makefile.gcc" + m = "#{zsrc}/win32/Makefile.gcc" + zmk += " PREFIX=" + zmk << CONFIG['CC'][/(.*-)gcc([^\/]*)\z/, 1] + zmk << " CC=$(CC)" if $2 + end + m = File.read(m) + zimplib = m[/^IMPLIB[ \t]*=[ \t]*(\S+)/, 1] + ($LOCAL_LIBS << " ./" << zimplib).strip! + unless $nmake or /^TOP[ \t]/ =~ m + m.gsub!(/win32\/zlib\.def/, '$(TOP)/\&') + m.gsub!(/^(\t.*[ \t])(\S+\.rc)/, '\1-I$(<D) $<') + m = "TOP = .\n""VPATH=$(TOP)\n" + m + zmkfile = File.basename(zmkfile) + File.rename(zmkfile, zmkfile+".orig") if File.exist?(zmkfile) + File.write(zmkfile, m) + end + addconf.push( + "ZMKFILE = #{zmkfile}\n", + "ZIMPLIB = #{zimplib}\n", + "ZOPTS = #{zopts.join(' ')}\n", + "$(TARGET_SO): $(ZIMPLIB)\n", + "$(ZIMPLIB):\n", + "#{zmk} $(ZOPTS) $@\n", + "install-so static: $(topdir)/#{dll}", + "$(topdir)/#{dll}: $(ZIMPLIB)\n", + "\t$(Q) $(COPY) #{dll} $(@D)\n", + "clean: clean-zsrc\n", + "clean-zsrc:\n", + "#{zmk} clean\n", + ) + end + Logging.message "using zlib in #{zsrc}\n" + $defs << "-DHAVE_ZLIB_H" + have_zlib = true + end +end +if have_zlib defines = [] Logging::message 'checking for kind of operating system... ' @@ -53,10 +116,29 @@ if %w'z libz zlib1 zlib zdll zlibwapi'.find {|z| have_library(z, 'deflateReset') $defs.concat(defines.collect{|d|' -D'+d}) - have_func('crc32_combine', 'zlib.h') - have_func('adler32_combine', 'zlib.h') - have_type('z_crc_t', 'zlib.h') + if zsrc + $defs << "-DHAVE_CRC32_COMBINE" + $defs << "-DHAVE_ADLER32_COMBINE" + $defs << "-DHAVE_TYPE_Z_CRC_T" + $defs << "-DHAVE_CRC32_Z" + $defs << "-DHAVE_ADLER32_Z" + $defs << "-DHAVE_ZLIB_SIZE_T_FUNCS" + else + have_func('crc32_combine', 'zlib.h') + have_func('adler32_combine', 'zlib.h') + have_type('z_crc_t', 'zlib.h') + if (have_type('z_size_t', 'zlib.h') && + have_func('crc32_z', 'zlib.h') && + have_func('adler32_z', 'zlib.h')) + $defs << "-DHAVE_ZLIB_SIZE_T_FUNCS" + end + end - create_makefile('zlib') + create_makefile('zlib') {|conf| + if zsrc + conf.concat addconf if addconf + end + conf + } end diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 4dfa2a5f34..481d74b2b6 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -25,7 +25,11 @@ # define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0 #endif -#define RUBY_ZLIB_VERSION "0.6.0" +#define RUBY_ZLIB_VERSION "3.2.3" + +#ifndef RB_PASS_CALLED_KEYWORDS +# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass) +#endif #ifndef GZIP_SUPPORT #define GZIP_SUPPORT 1 @@ -40,6 +44,14 @@ #endif #endif +#if defined(HAVE_ZLIB_SIZE_T_FUNCS) +typedef uLong (*checksum_func)(uLong, const Bytef*, z_size_t); +# define crc32 crc32_z +# define adler32 adler32_z +#else +typedef uLong (*checksum_func)(uLong, const Bytef*, uInt); +#endif + #if SIZEOF_LONG > SIZEOF_INT static inline uInt max_uint(long n) @@ -52,13 +64,16 @@ max_uint(long n) #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)); +static VALUE do_checksum(int, VALUE*, checksum_func); static VALUE rb_zlib_adler32(int, VALUE*, VALUE); static VALUE rb_zlib_crc32(int, VALUE*, VALUE); static VALUE rb_zlib_crc_table(VALUE); @@ -72,9 +87,10 @@ struct zstream_run_args; static void zstream_init(struct zstream*, const struct zstream_funcs*); static void zstream_expand_buffer(struct zstream*); static void zstream_expand_buffer_into(struct zstream*, unsigned long); +static int zstream_expand_buffer_non_stream(struct zstream *z); static void zstream_append_buffer(struct zstream*, const Bytef*, long); static VALUE zstream_detach_buffer(struct zstream*); -static VALUE zstream_shift_buffer(struct zstream*, long); +static VALUE zstream_shift_buffer(struct zstream*, long, VALUE); static void zstream_buffer_ungets(struct zstream*, const Bytef*, unsigned long); static void zstream_buffer_ungetbyte(struct zstream*, int); static void zstream_append_input(struct zstream*, const Bytef*, long); @@ -84,6 +100,7 @@ static void zstream_passthrough_input(struct zstream*); static VALUE zstream_detach_input(struct zstream*); static void zstream_reset(struct zstream*); static VALUE zstream_end(struct zstream*); +static VALUE zstream_ensure_end(VALUE v); static void zstream_run(struct zstream*, Bytef*, long, int); static VALUE zstream_sync(struct zstream*, Bytef*, long); static void zstream_mark(void*); @@ -124,7 +141,7 @@ 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); @@ -139,22 +156,22 @@ static void gzfile_reset(struct gzfile*); static void gzfile_close(struct gzfile*, int); static void gzfile_write_raw(struct gzfile*); static VALUE gzfile_read_raw_partial(VALUE); -static VALUE gzfile_read_raw_rescue(VALUE); -static VALUE gzfile_read_raw(struct gzfile*); -static int gzfile_read_raw_ensure(struct gzfile*, long); +static VALUE gzfile_read_raw_rescue(VALUE,VALUE); +static VALUE gzfile_read_raw(struct gzfile*, VALUE outbuf); +static int gzfile_read_raw_ensure(struct gzfile*, long, VALUE outbuf); static char *gzfile_read_raw_until_zero(struct gzfile*, long); static unsigned int gzfile_get16(const unsigned char*); static unsigned long gzfile_get32(const unsigned char*); static void gzfile_set32(unsigned long n, unsigned char*); static void gzfile_make_header(struct gzfile*); static void gzfile_make_footer(struct gzfile*); -static void gzfile_read_header(struct gzfile*); -static void gzfile_check_footer(struct gzfile*); +static void gzfile_read_header(struct gzfile*, VALUE outbuf); +static void gzfile_check_footer(struct gzfile*, VALUE outbuf); static void gzfile_write(struct gzfile*, Bytef*, long); -static long gzfile_read_more(struct gzfile*); +static long gzfile_read_more(struct gzfile*, VALUE outbuf); static void gzfile_calc_crc(struct gzfile*, VALUE); -static VALUE gzfile_read(struct gzfile*, long); -static VALUE gzfile_read_all(struct gzfile*); +static VALUE gzfile_read(struct gzfile*, long, VALUE); +static VALUE gzfile_read_all(struct gzfile*, VALUE); static void gzfile_ungets(struct gzfile*, const Bytef*, long); static void gzfile_ungetbyte(struct gzfile*, int); static VALUE gzfile_writer_end_run(VALUE); @@ -196,7 +213,7 @@ static VALUE rb_gzwriter_s_allocate(VALUE); static VALUE rb_gzwriter_s_open(int, VALUE*, VALUE); static VALUE rb_gzwriter_initialize(int, VALUE*, VALUE); static VALUE rb_gzwriter_flush(int, VALUE*, VALUE); -static VALUE rb_gzwriter_write(VALUE, VALUE); +static VALUE rb_gzwriter_write(int, VALUE*, VALUE); static VALUE rb_gzwriter_putc(VALUE, VALUE); static VALUE rb_gzreader_s_allocate(VALUE); @@ -279,6 +296,7 @@ static VALUE rb_gzreader_readlines(int, VALUE*, VALUE); * - Zlib::MemError * - Zlib::BufError * - Zlib::VersionError + * - Zlib::InProgressError * * (if you have GZIP_SUPPORT) * - Zlib::GzipReader @@ -295,7 +313,7 @@ void Init_zlib(void); /*--------- Exceptions --------*/ static VALUE cZError, cStreamEnd, cNeedDict; -static VALUE cStreamError, cDataError, cMemError, cBufError, cVersionError; +static VALUE cStreamError, cDataError, cMemError, cBufError, cVersionError, cInProgressError; static void raise_zlib_error(int err, const char *msg) @@ -345,7 +363,9 @@ raise_zlib_error(int err, const char *msg) static void finalizer_warn(const char *msg) { +#if 0 fprintf(stderr, "zlib(finalizer): %s\n", msg); +#endif } @@ -359,33 +379,35 @@ finalizer_warn(const char *msg) static VALUE rb_zlib_version(VALUE klass) { - VALUE str; - - str = rb_str_new2(zlibVersion()); - OBJ_TAINT(str); /* for safe */ - return str; + return rb_str_new2(zlibVersion()); } -#if SIZEOF_LONG > SIZEOF_INT +#if SIZEOF_LONG * CHAR_BIT > 32 +# define mask32(x) ((x) & 0xffffffff) +#else +# define mask32(x) (x) +#endif + +#if SIZEOF_LONG > SIZEOF_INT && !defined(HAVE_ZLIB_SIZE_T_FUNCS) static uLong checksum_long(uLong (*func)(uLong, const Bytef*, uInt), uLong sum, const Bytef *ptr, long len) { if (len > UINT_MAX) { do { - sum = func(sum, ptr, UINT_MAX); + sum = func(mask32(sum), ptr, UINT_MAX); ptr += UINT_MAX; len -= UINT_MAX; } while (len >= UINT_MAX); } - if (len > 0) sum = func(sum, ptr, (uInt)len); + if (len > 0) sum = func(mask32(sum), ptr, (uInt)len); return sum; } #else -#define checksum_long(func, sum, ptr, len) (func)((sum), (ptr), (len)) +#define checksum_long(func, sum, ptr, len) (func)(mask32(sum), (ptr), (len)) #endif static VALUE -do_checksum(int argc, VALUE *argv, uLong (*func)(uLong, const Bytef*, uInt)) +do_checksum(int argc, VALUE *argv, checksum_func func) { VALUE str, vsum; unsigned long sum; @@ -403,7 +425,16 @@ do_checksum(int argc, VALUE *argv, uLong (*func)(uLong, const Bytef*, uInt)) } if (NIL_P(str)) { - sum = func(sum, Z_NULL, 0); + sum = func(mask32(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); @@ -420,6 +451,8 @@ do_checksum(int argc, VALUE *argv, uLong (*func)(uLong, const Bytef*, uInt)) * 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: * @@ -442,7 +475,7 @@ rb_zlib_adler32(int argc, VALUE *argv, VALUE klass) * * call-seq: Zlib.adler32_combine(adler1, adler2, len2) * - * Combine two Adler-32 check values in to one. +alder1+ is the first Adler-32 + * Combine two Adler-32 check values in to one. +adler1+ is the first Adler-32 * value, +adler2+ is the second Adler-32 value. +len2+ is the length of the * string used to generate +adler2+. * @@ -450,7 +483,7 @@ rb_zlib_adler32(int argc, VALUE *argv, VALUE klass) static VALUE rb_zlib_adler32_combine(VALUE klass, VALUE adler1, VALUE adler2, VALUE len2) { - return ULONG2NUM( + return ULONG2NUM( adler32_combine(NUM2ULONG(adler1), NUM2ULONG(adler2), NUM2LONG(len2))); } #else @@ -464,7 +497,9 @@ rb_zlib_adler32_combine(VALUE klass, VALUE adler1, VALUE adler2, VALUE len2) * * 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. */ @@ -488,7 +523,7 @@ rb_zlib_crc32(int argc, VALUE *argv, VALUE klass) static VALUE rb_zlib_crc32_combine(VALUE klass, VALUE crc1, VALUE crc2, VALUE len2) { - return ULONG2NUM( + return ULONG2NUM( crc32_combine(NUM2ULONG(crc1), NUM2ULONG(crc2), NUM2LONG(len2))); } #else @@ -527,8 +562,8 @@ rb_zlib_crc_table(VALUE obj) struct zstream { unsigned long flags; VALUE buf; - long buf_filled; VALUE input; + VALUE mutex; z_stream stream; const struct zstream_funcs { int (*reset)(z_streamp); @@ -537,19 +572,24 @@ struct zstream { } *func; }; -#define ZSTREAM_FLAG_READY 0x1 -#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 +#define ZSTREAM_FLAG_READY (1 << 0) +#define ZSTREAM_FLAG_IN_STREAM (1 << 1) +#define ZSTREAM_FLAG_FINISHED (1 << 2) +#define ZSTREAM_FLAG_CLOSING (1 << 3) +#define ZSTREAM_FLAG_GZFILE (1 << 4) /* disallows yield from expand_buffer for gzip*/ -#define ZSTREAM_FLAG_UNUSED 0x20 +#define ZSTREAM_REUSE_BUFFER (1 << 5) +#define ZSTREAM_IN_PROGRESS (1 << 6) +#define ZSTREAM_FLAG_UNUSED (1 << 7) #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 @@ -569,7 +609,9 @@ static const struct zstream_funcs inflate_funcs = { }; struct zstream_run_args { - struct zstream * z; + struct zstream *const z; + Bytef *src; + long len; int flush; /* stream flush value for inflate() or deflate() */ int interrupt; /* stop processing the stream and return to ruby */ int jump_state; /* for buffer expansion block break or exception */ @@ -599,8 +641,8 @@ zstream_init(struct zstream *z, const struct zstream_funcs *func) { z->flags = 0; z->buf = Qnil; - z->buf_filled = 0; z->input = Qnil; + z->mutex = rb_mutex_new(); z->stream.zalloc = zlib_mem_alloc; z->stream.zfree = zlib_mem_free; z->stream.opaque = Z_NULL; @@ -624,17 +666,23 @@ zstream_expand_buffer(struct zstream *z) } if (!ZSTREAM_IS_GZFILE(z) && rb_block_given_p()) { - if (z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) { + long buf_filled = ZSTREAM_BUF_FILLED(z); + if (buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) { int state = 0; - VALUE self = (VALUE)z->stream.opaque; - rb_str_resize(z->buf, z->buf_filled); - rb_obj_reveal(z->buf, rb_cString); - OBJ_INFECT(z->buf, self); + if (!ZSTREAM_REUSE_BUFFER_P(z)) { + rb_obj_reveal(z->buf, rb_cString); + } - rb_protect(rb_yield, z->buf, &state); + 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) @@ -644,23 +692,11 @@ zstream_expand_buffer(struct zstream *z) } else { zstream_expand_buffer_into(z, - ZSTREAM_AVAIL_OUT_STEP_MAX - z->buf_filled); + ZSTREAM_AVAIL_OUT_STEP_MAX - buf_filled); } } else { - if (RSTRING_LEN(z->buf) - z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) { - z->stream.avail_out = ZSTREAM_AVAIL_OUT_STEP_MAX; - } - else { - long inc = z->buf_filled / 2; - if (inc < ZSTREAM_AVAIL_OUT_STEP_MIN) { - inc = ZSTREAM_AVAIL_OUT_STEP_MIN; - } - rb_str_resize(z->buf, z->buf_filled + inc); - z->stream.avail_out = (inc < ZSTREAM_AVAIL_OUT_STEP_MAX) ? - (int)inc : ZSTREAM_AVAIL_OUT_STEP_MAX; - } - z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled; + zstream_expand_buffer_non_stream(z); } } @@ -670,59 +706,47 @@ zstream_expand_buffer_into(struct zstream *z, unsigned long size) if (NIL_P(z->buf)) { /* I uses rb_str_new here not rb_str_buf_new because rb_str_buf_new makes a zero-length string. */ - z->buf = rb_str_new(0, size); - z->buf_filled = 0; + z->buf = rb_str_buf_new(size); z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf); z->stream.avail_out = MAX_UINT(size); rb_obj_hide(z->buf); } else if (z->stream.avail_out != size) { - rb_str_resize(z->buf, z->buf_filled + size); - z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled; + rb_str_modify_expand(z->buf, size); + z->stream.next_out = (Bytef*)RSTRING_END(z->buf); z->stream.avail_out = MAX_UINT(size); } } -static void * -zstream_expand_buffer_protect(void *ptr) +static int +zstream_expand_buffer_protect(struct zstream *z) { - struct zstream *z = (struct zstream *)ptr; int state = 0; rb_protect((VALUE (*)(VALUE))zstream_expand_buffer, (VALUE)z, &state); - return (void *)(VALUE)state; + return state; } static int -zstream_expand_buffer_without_gvl(struct zstream *z) +zstream_expand_buffer_non_stream(struct zstream *z) { - char * new_str; - long inc, len; + long inc, len = ZSTREAM_BUF_FILLED(z); - if (RSTRING_LEN(z->buf) - z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) { + if (rb_str_capacity(z->buf) - len >= ZSTREAM_AVAIL_OUT_STEP_MAX) { z->stream.avail_out = ZSTREAM_AVAIL_OUT_STEP_MAX; } else { - inc = z->buf_filled / 2; + inc = len / 2; if (inc < ZSTREAM_AVAIL_OUT_STEP_MIN) { inc = ZSTREAM_AVAIL_OUT_STEP_MIN; } - len = z->buf_filled + inc; - - new_str = ruby_xrealloc(RSTRING(z->buf)->as.heap.ptr, len + 1); - - /* from rb_str_resize */ - RSTRING(z->buf)->as.heap.ptr = new_str; - RSTRING(z->buf)->as.heap.ptr[len] = '\0'; /* sentinel */ - RSTRING(z->buf)->as.heap.len = - RSTRING(z->buf)->as.heap.aux.capa = len; - + rb_str_modify_expand(z->buf, inc); z->stream.avail_out = (inc < ZSTREAM_AVAIL_OUT_STEP_MAX) ? (int)inc : ZSTREAM_AVAIL_OUT_STEP_MAX; } - z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled; + z->stream.next_out = (Bytef*)RSTRING_END(z->buf); return ZSTREAM_EXPAND_BUFFER_OK; } @@ -733,15 +757,14 @@ zstream_append_buffer(struct zstream *z, const Bytef *src, long len) if (NIL_P(z->buf)) { z->buf = rb_str_buf_new(len); rb_str_buf_cat(z->buf, (const char*)src, len); - z->buf_filled = len; z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf); z->stream.avail_out = 0; rb_obj_hide(z->buf); return; } - if (RSTRING_LEN(z->buf) < z->buf_filled + len) { - rb_str_resize(z->buf, z->buf_filled + len); + if ((long)rb_str_capacity(z->buf) < ZSTREAM_BUF_FILLED(z) + len) { + rb_str_modify_expand(z->buf, len); z->stream.avail_out = 0; } else { @@ -752,9 +775,8 @@ zstream_append_buffer(struct zstream *z, const Bytef *src, long len) z->stream.avail_out = 0; } } - memcpy(RSTRING_PTR(z->buf) + z->buf_filled, src, len); - z->buf_filled += len; - z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled; + rb_str_cat(z->buf, (const char *)src, len); + z->stream.next_out = (Bytef*)RSTRING_END(z->buf); } #define zstream_append_buffer2(z,v) \ @@ -763,7 +785,7 @@ zstream_append_buffer(struct zstream *z, const Bytef *src, long len) static VALUE zstream_detach_buffer(struct zstream *z) { - VALUE dst, self = (VALUE)z->stream.opaque; + VALUE dst; if (!ZSTREAM_IS_FINISHED(z) && !ZSTREAM_IS_GZFILE(z) && rb_block_given_p()) { @@ -777,14 +799,12 @@ zstream_detach_buffer(struct zstream *z) } else { dst = z->buf; - rb_str_resize(dst, z->buf_filled); - rb_obj_reveal(dst, rb_cString); + if (!ZSTREAM_REUSE_BUFFER_P(z)) { + rb_obj_reveal(dst, rb_cString); + } } - OBJ_INFECT(dst, self); - z->buf = Qnil; - z->buf_filled = 0; z->stream.next_out = 0; z->stream.avail_out = 0; @@ -797,21 +817,35 @@ zstream_detach_buffer(struct zstream *z) } static VALUE -zstream_shift_buffer(struct zstream *z, long len) -{ - VALUE dst; - long buflen; - - if (z->buf_filled <= len) { - return zstream_detach_buffer(z); +zstream_shift_buffer(struct zstream *z, long len, VALUE dst) +{ + char *bufptr; + long buflen = ZSTREAM_BUF_FILLED(z); + + if (buflen <= len) { + if (NIL_P(dst) || (!ZSTREAM_IS_FINISHED(z) && !ZSTREAM_IS_GZFILE(z) && + rb_block_given_p())) { + return zstream_detach_buffer(z); + } else { + bufptr = RSTRING_PTR(z->buf); + rb_str_resize(dst, buflen); + memcpy(RSTRING_PTR(dst), bufptr, buflen); + } + buflen = 0; + } else { + bufptr = RSTRING_PTR(z->buf); + if (NIL_P(dst)) { + dst = rb_str_new(bufptr, len); + } else { + rb_str_resize(dst, len); + memcpy(RSTRING_PTR(dst), bufptr, len); + } + buflen -= len; } - - dst = rb_str_new(RSTRING_PTR(z->buf), len); - z->buf_filled -= len; - memmove(RSTRING_PTR(z->buf), RSTRING_PTR(z->buf) + len, - z->buf_filled); - z->stream.next_out = (Bytef*)RSTRING_PTR(z->buf) + z->buf_filled; - buflen = RSTRING_LEN(z->buf) - z->buf_filled; + memmove(bufptr, bufptr + len, buflen); + rb_str_set_len(z->buf, buflen); + z->stream.next_out = (Bytef*)RSTRING_END(z->buf); + buflen = (long)rb_str_capacity(z->buf) - ZSTREAM_BUF_FILLED(z); if (buflen > ZSTREAM_AVAIL_OUT_STEP_MAX) { buflen = ZSTREAM_AVAIL_OUT_STEP_MAX; } @@ -823,13 +857,15 @@ zstream_shift_buffer(struct zstream *z, long len) static void zstream_buffer_ungets(struct zstream *z, const Bytef *b, unsigned long len) { - if (NIL_P(z->buf) || RSTRING_LEN(z->buf) - z->buf_filled == 0) { - zstream_expand_buffer_into(z, len); - } + char *bufptr; + long filled; - memmove(RSTRING_PTR(z->buf) + len, RSTRING_PTR(z->buf), z->buf_filled); - memmove(RSTRING_PTR(z->buf), b, len); - z->buf_filled+=len; + zstream_expand_buffer_into(z, len); + + RSTRING_GETMEM(z->buf, bufptr, filled); + memmove(bufptr + len, bufptr, filled); + memmove(bufptr, b, len); + rb_str_set_len(z->buf, filled + len); if (z->stream.avail_out > 0) { if (len > z->stream.avail_out) len = z->stream.avail_out; z->stream.next_out+=len; @@ -840,17 +876,8 @@ zstream_buffer_ungets(struct zstream *z, const Bytef *b, unsigned long len) static void zstream_buffer_ungetbyte(struct zstream *z, int c) { - if (NIL_P(z->buf) || RSTRING_LEN(z->buf) - z->buf_filled == 0) { - zstream_expand_buffer(z); - } - - memmove(RSTRING_PTR(z->buf) + 1, RSTRING_PTR(z->buf), z->buf_filled); - RSTRING_PTR(z->buf)[0] = (char)c; - z->buf_filled++; - if (z->stream.avail_out > 0) { - z->stream.next_out++; - z->stream.avail_out--; - } + Bytef cc = (Bytef)c; + zstream_buffer_ungets(z, &cc, 1); } static void @@ -875,20 +902,49 @@ zstream_append_input(struct zstream *z, const Bytef *src, long len) static void zstream_discard_input(struct zstream *z, long len) { - if (NIL_P(z->input) || RSTRING_LEN(z->input) <= len) { - z->input = Qnil; + if (NIL_P(z->input)) { } - else { - memmove(RSTRING_PTR(z->input), RSTRING_PTR(z->input) + len, - RSTRING_LEN(z->input) - len); - rb_str_resize(z->input, RSTRING_LEN(z->input) - len); + else if (RBASIC_CLASS(z->input) == 0) { + /* hidden, we created z->input and have complete control */ + char *ptr; + long oldlen, newlen; + + RSTRING_GETMEM(z->input, ptr, oldlen); + newlen = oldlen - len; + if (newlen > 0) { + memmove(ptr, ptr + len, newlen); + } + if (newlen < 0) { + newlen = 0; + } + rb_str_resize(z->input, newlen); + if (newlen == 0) { + z->input = Qnil; + } + else { + rb_str_set_len(z->input, newlen); + } + } + else { /* do not mangle user-provided data */ + if (RSTRING_LEN(z->input) <= len) { + z->input = Qnil; + } + else { + z->input = rb_str_subseq(z->input, len, + RSTRING_LEN(z->input) - len); + } } } static void zstream_reset_input(struct zstream *z) { - z->input = Qnil; + if (!NIL_P(z->input) && RBASIC_CLASS(z->input) == 0) { + rb_str_resize(z->input, 0); + } + else { + z->input = Qnil; + } } static void @@ -913,7 +969,6 @@ zstream_detach_input(struct zstream *z) rb_obj_reveal(dst, rb_cString); } z->input = Qnil; - rb_obj_reveal(dst, rb_cString); return dst; } @@ -928,7 +983,6 @@ zstream_reset(struct zstream *z) } z->flags = ZSTREAM_FLAG_READY; z->buf = Qnil; - z->buf_filled = 0; z->stream.next_out = 0; z->stream.avail_out = 0; zstream_reset_input(z); @@ -957,83 +1011,138 @@ zstream_end(struct zstream *z) return Qnil; } +static VALUE +zstream_ensure_end(VALUE v) +{ + return zstream_end((struct zstream *)v); +} + static void * -zstream_run_func(void *ptr) +zstream_run_once(void *_arguments) +{ + struct zstream_run_args *arguments = (struct zstream_run_args *)_arguments; + struct zstream *z = arguments->z; + + uintptr_t error = z->func->run(&z->stream, arguments->flush); + + return (void*)error; +} + +/* + * There is no safe way to interrupt z->run->func(). + * async-signal-safe + */ +static void +zstream_unblock_func(void *ptr) { struct zstream_run_args *args = (struct zstream_run_args *)ptr; - int err, state, flush = args->flush; + + args->interrupt = 1; +} + +#ifndef RB_NOGVL_OFFLOAD_SAFE +// Default to no-op if it's not defined: +#define RB_NOGVL_OFFLOAD_SAFE 0 +#endif + +static VALUE +zstream_run_once_begin(VALUE _arguments) +{ + struct zstream_run_args *arguments = (struct zstream_run_args *)_arguments; + struct zstream *z = arguments->z; + + rb_str_locktmp(z->buf); + +#ifndef RB_NOGVL_UBF_ASYNC_SAFE + return (VALUE)rb_thread_call_without_gvl(zstream_run_once, (void *)arguments, zstream_unblock_func, (void *)arguments); +#else + return (VALUE)rb_nogvl(zstream_run_once, (void *)arguments, zstream_unblock_func, (void *)arguments, RB_NOGVL_UBF_ASYNC_SAFE | RB_NOGVL_OFFLOAD_SAFE); +#endif +} + +static VALUE +zstream_run_once_ensure(VALUE _arguments) +{ + struct zstream_run_args *arguments = (struct zstream_run_args *)_arguments; + struct zstream *z = arguments->z; + + rb_str_unlocktmp(z->buf); + + return Qnil; +} + +static int +zstream_run_func(struct zstream_run_args *args) +{ struct zstream *z = args->z; + int state; uInt n; - err = Z_OK; + int err = Z_OK; while (!args->interrupt) { - n = z->stream.avail_out; - err = z->func->run(&z->stream, flush); - z->buf_filled += n - z->stream.avail_out; + n = z->stream.avail_out; - if (err == Z_STREAM_END) { - z->flags &= ~ZSTREAM_FLAG_IN_STREAM; - z->flags |= ZSTREAM_FLAG_FINISHED; - break; - } + err = (int)(VALUE)rb_ensure(zstream_run_once_begin, (VALUE)args, zstream_run_once_ensure, (VALUE)args); - if (err != Z_OK && err != Z_BUF_ERROR) - break; + rb_str_set_len(z->buf, ZSTREAM_BUF_FILLED(z) + (n - z->stream.avail_out)); - if (z->stream.avail_out > 0) { - z->flags |= ZSTREAM_FLAG_IN_STREAM; - break; - } + if (err == Z_STREAM_END) { + z->flags &= ~ZSTREAM_FLAG_IN_STREAM; + z->flags |= ZSTREAM_FLAG_FINISHED; + break; + } - if (z->stream.avail_in == 0 && z->func == &inflate_funcs) { - /* break here because inflate() return Z_BUF_ERROR when avail_in == 0. */ - /* but deflate() could be called with avail_in == 0 (there's hidden buffer - in zstream->state) */ - z->flags |= ZSTREAM_FLAG_IN_STREAM; - break; - } + if (err != Z_OK && err != Z_BUF_ERROR) { + break; + } - if (args->stream_output) { - state = (int)(VALUE)rb_thread_call_with_gvl(zstream_expand_buffer_protect, - (void *)z); - } - else { - state = zstream_expand_buffer_without_gvl(z); - } + if (z->stream.avail_out > 0) { + z->flags |= ZSTREAM_FLAG_IN_STREAM; + break; + } - if (state) { - err = Z_OK; /* buffer expanded but stream processing was stopped */ - args->jump_state = state; - break; - } - } + if (z->stream.avail_in == 0 && z->func == &inflate_funcs) { + /* break here because inflate() return Z_BUF_ERROR when avail_in == 0. */ + /* but deflate() could be called with avail_in == 0 (there's hidden buffer + in zstream->state) */ + z->flags |= ZSTREAM_FLAG_IN_STREAM; + break; + } - return (void *)(VALUE)err; -} + if (args->stream_output) { + state = zstream_expand_buffer_protect(z); + } + else { + state = zstream_expand_buffer_non_stream(z); + } -/* - * There is no safe way to interrupt z->run->func(). - */ -static void -zstream_unblock_func(void *ptr) -{ - struct zstream_run_args *args = (struct zstream_run_args *)ptr; + if (state) { + err = Z_OK; /* buffer expanded but stream processing was stopped */ + args->jump_state = state; + break; + } + } - args->interrupt = 1; + return err; } -static void -zstream_run(struct zstream *z, Bytef *src, long len, int flush) +static VALUE +zstream_run_try(VALUE value_arg) { - struct zstream_run_args args; + struct zstream_run_args *args = (struct zstream_run_args *)value_arg; + struct zstream *z = args->z; + Bytef *src = args->src; + long len = args->len; + int flush = args->flush; + int err; - VALUE guard = Qnil; + VALUE old_input = Qnil; - args.z = z; - args.flush = flush; - args.interrupt = 0; - args.jump_state = 0; - args.stream_output = !ZSTREAM_IS_GZFILE(z) && rb_block_given_p(); + /* Cannot start zstream while it is in progress. */ + if (z->flags & ZSTREAM_IN_PROGRESS) { + rb_raise(cInProgressError, "zlib stream is in progress"); + } + z->flags |= ZSTREAM_IN_PROGRESS; if (NIL_P(z->input) && len == 0) { z->stream.next_in = (Bytef*)""; @@ -1041,12 +1150,13 @@ zstream_run(struct zstream *z, Bytef *src, long len, int flush) } else { zstream_append_input(z, src, len); - z->stream.next_in = (Bytef*)RSTRING_PTR(z->input); - z->stream.avail_in = MAX_UINT(RSTRING_LEN(z->input)); /* keep reference to `z->input' so as not to be garbage collected after zstream_reset_input() and prevent `z->stream.next_in' from dangling. */ - guard = z->input; + old_input = zstream_detach_input(z); + rb_obj_hide(old_input); /* for GVL release and later recycle */ + z->stream.next_in = (Bytef*)RSTRING_PTR(old_input); + z->stream.avail_in = MAX_UINT(RSTRING_LEN(old_input)); } if (z->stream.avail_out == 0) { @@ -1054,12 +1164,22 @@ zstream_run(struct zstream *z, Bytef *src, long len, int flush) } loop: - err = (int)(VALUE)rb_thread_call_without_gvl(zstream_run_func, (void *)&args, - zstream_unblock_func, (void *)&args); + err = zstream_run_func(args); + + /* retry if no exception is thrown */ + if (err == Z_OK && args->interrupt) { + args->interrupt = 0; + + /* Retry only if both avail_in > 0 (more input to process) and avail_out > 0 + * (output buffer has space). If avail_out == 0, the buffer is full and should + * be consumed by the caller first. If avail_in == 0, there's nothing more to process. */ + if (z->stream.avail_in > 0 && z->stream.avail_out > 0) { + goto loop; + } + } - if (flush != Z_FINISH && err == Z_BUF_ERROR - && z->stream.avail_out > 0) { - z->flags |= ZSTREAM_FLAG_IN_STREAM; + if (flush != Z_FINISH && err == Z_BUF_ERROR && z->stream.avail_out > 0) { + z->flags |= ZSTREAM_FLAG_IN_STREAM; } zstream_reset_input(z); @@ -1084,9 +1204,40 @@ loop: if (z->stream.avail_in > 0) { zstream_append_input(z, z->stream.next_in, z->stream.avail_in); - RB_GC_GUARD(guard); /* prevent tail call to make guard effective */ + } + if (!NIL_P(old_input)) { + rb_str_resize(old_input, 0); } + return Qnil; +} + +static VALUE +zstream_run_ensure(VALUE value_arg) +{ + struct zstream_run_args *args = (struct zstream_run_args *)value_arg; + struct zstream *z = args->z; + + /* Remove ZSTREAM_IN_PROGRESS flag to signal that this zstream is not in use. */ + z->flags &= ~ZSTREAM_IN_PROGRESS; + + return Qnil; +} + +static void +zstream_run(struct zstream *z, Bytef *src, long len, int flush) +{ + struct zstream_run_args args = { + .z = z, + .src = src, + .len = len, + .flush = flush, + .interrupt = 0, + .jump_state = 0, + .stream_output = !ZSTREAM_IS_GZFILE(z) && rb_block_given_p(), + }; + + rb_ensure(zstream_run_try, (VALUE)&args, zstream_run_ensure, (VALUE)&args); if (args.jump_state) rb_jump_tag(args.jump_state); } @@ -1136,6 +1287,7 @@ zstream_mark(void *p) struct zstream *z = p; rb_gc_mark(z->buf); rb_gc_mark(z->input); + rb_gc_mark(z->mutex); } static void @@ -1308,6 +1460,7 @@ rb_zstream_finish(VALUE obj) * call-seq: * flush_next_in -> input * + * Flushes input buffer and returns all data in that buffer. */ static VALUE rb_zstream_flush_next_in(VALUE obj) @@ -1317,7 +1470,6 @@ rb_zstream_flush_next_in(VALUE obj) TypedData_Get_Struct(obj, struct zstream, &zstream_data_type, z); dst = zstream_detach_input(z); - OBJ_INFECT(dst, obj); return dst; } @@ -1397,7 +1549,7 @@ rb_zstream_total_out(VALUE obj) } /* - * Guesses the type of the data which have been inputed into the stream. The + * Guesses the type of the data which have been inputted into the stream. The * returned value is either <tt>BINARY</tt>, <tt>ASCII</tt>, or * <tt>UNKNOWN</tt>. */ @@ -1413,7 +1565,7 @@ rb_zstream_data_type(VALUE obj) static VALUE rb_zstream_adler(VALUE obj) { - return rb_uint2inum(get_zstream(obj)->stream.adler); + return rb_uint2inum(get_zstream(obj)->stream.adler); } /* @@ -1578,7 +1730,6 @@ rb_deflate_init_copy(VALUE self, VALUE orig) } z1->input = NIL_P(z2->input) ? Qnil : rb_str_dup(z2->input); z1->buf = NIL_P(z2->buf) ? Qnil : rb_str_dup(z2->buf); - z1->buf_filled = z2->buf_filled; z1->flags = z2->flags; return self; @@ -1637,9 +1788,8 @@ rb_deflate_s_deflate(int argc, VALUE *argv, VALUE klass) args[0] = (VALUE)&z; args[1] = src; - dst = rb_ensure(deflate_run, (VALUE)args, zstream_end, (VALUE)&z); + dst = rb_ensure(deflate_run, (VALUE)args, zstream_ensure_end, (VALUE)&z); - OBJ_INFECT(dst, src); return dst; } @@ -1656,6 +1806,22 @@ do_deflate(struct zstream *z, VALUE src, int flush) } } +struct rb_zlib_deflate_arguments { + struct zstream *z; + VALUE src; + int flush; +}; + +static VALUE +rb_deflate_deflate_body(VALUE args) +{ + struct rb_zlib_deflate_arguments *arguments = (struct rb_zlib_deflate_arguments *)args; + + do_deflate(arguments->z, arguments->src, arguments->flush); + + return zstream_detach_buffer(arguments->z); +} + /* * Document-method: Zlib::Deflate#deflate * @@ -1687,12 +1853,10 @@ rb_deflate_deflate(int argc, VALUE *argv, VALUE obj) { struct zstream *z = get_zstream(obj); VALUE src, flush; - rb_scan_args(argc, argv, "11", &src, &flush); - OBJ_INFECT(obj, src); - do_deflate(z, src, ARG_FLUSH(flush)); + struct rb_zlib_deflate_arguments arguments = {z, src, ARG_FLUSH(flush)}; - return zstream_detach_buffer(z); + return rb_mutex_synchronize(z->mutex, rb_deflate_deflate_body, (VALUE)&arguments); } /* @@ -1707,7 +1871,6 @@ rb_deflate_deflate(int argc, VALUE *argv, VALUE obj) static VALUE rb_deflate_addstr(VALUE obj, VALUE src) { - OBJ_INFECT(obj, src); do_deflate(get_zstream(obj), src, Z_NO_FLUSH); return obj; } @@ -1762,23 +1925,26 @@ rb_deflate_params(VALUE obj, VALUE v_level, VALUE v_strategy) int level, strategy; int err; uInt n; + long filled; level = ARG_LEVEL(v_level); strategy = ARG_STRATEGY(v_strategy); n = z->stream.avail_out; err = deflateParams(&z->stream, level, strategy); - z->buf_filled += n - z->stream.avail_out; + filled = n - z->stream.avail_out; while (err == Z_BUF_ERROR) { rb_warning("deflateParams() returned Z_BUF_ERROR"); zstream_expand_buffer(z); + rb_str_set_len(z->buf, RSTRING_LEN(z->buf) + filled); n = z->stream.avail_out; err = deflateParams(&z->stream, level, strategy); - z->buf_filled += n - z->stream.avail_out; + filled = n - z->stream.avail_out; } if (err != Z_OK) { raise_zlib_error(err, z->stream.msg); } + rb_str_set_len(z->buf, RSTRING_LEN(z->buf) + filled); return Qnil; } @@ -1804,7 +1970,6 @@ rb_deflate_set_dictionary(VALUE obj, VALUE dic) VALUE src = dic; int err; - OBJ_INFECT(obj, dic); StringValue(src); err = deflateSetDictionary(&z->stream, (Bytef*)RSTRING_PTR(src), RSTRING_LENINT(src)); @@ -1949,9 +2114,8 @@ rb_inflate_s_inflate(VALUE obj, VALUE src) args[0] = (VALUE)&z; args[1] = src; - dst = rb_ensure(inflate_run, (VALUE)args, zstream_end, (VALUE)&z); + dst = rb_ensure(inflate_run, (VALUE)args, zstream_ensure_end, (VALUE)&z); - OBJ_INFECT(dst, src); return dst; } @@ -1988,12 +2152,79 @@ rb_inflate_add_dictionary(VALUE obj, VALUE dictionary) return obj; } +struct rb_zlib_inflate_arguments { + struct zstream *z; + int argc; + VALUE *argv; +}; + +static VALUE +rb_inflate_inflate_body(VALUE _arguments) +{ + struct rb_zlib_inflate_arguments *arguments = (struct rb_zlib_inflate_arguments*)_arguments; + struct zstream *z = arguments->z; + int argc = arguments->argc; + VALUE *argv = arguments->argv; + 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); + 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); + if (ZSTREAM_IS_FINISHED(z)) { + zstream_passthrough_input(z); + } + } + + return dst; +} + /* * 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 @@ -2003,6 +2234,15 @@ rb_inflate_add_dictionary(VALUE obj, VALUE dictionary) * 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: @@ -2026,33 +2266,11 @@ rb_inflate_add_dictionary(VALUE obj, VALUE dictionary) * 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; - - OBJ_INFECT(obj, 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); - OBJ_INFECT(dst, obj); - } - } - else { - do_inflate(z, src); - dst = zstream_detach_buffer(z); - if (ZSTREAM_IS_FINISHED(z)) { - zstream_passthrough_input(z); - } - } - - return dst; + struct rb_zlib_inflate_arguments arguments = {z, argc, argv}; + return rb_mutex_synchronize(z->mutex, rb_inflate_inflate_body, (VALUE)&arguments); } /* @@ -2067,8 +2285,6 @@ rb_inflate_addstr(VALUE obj, VALUE src) { struct zstream *z = get_zstream(obj); - OBJ_INFECT(obj, src); - if (ZSTREAM_IS_FINISHED(z)) { if (!NIL_P(src)) { StringValue(src); @@ -2098,7 +2314,6 @@ rb_inflate_sync(VALUE obj, VALUE src) { struct zstream *z = get_zstream(obj); - OBJ_INFECT(obj, src); StringValue(src); return zstream_sync(z, (Bytef*)RSTRING_PTR(src), RSTRING_LEN(src)); } @@ -2140,7 +2355,6 @@ rb_inflate_set_dictionary(VALUE obj, VALUE dic) VALUE src = dic; int err; - OBJ_INFECT(obj, dic); StringValue(src); err = inflateSetDictionary(&z->stream, (Bytef*)RSTRING_PTR(src), RSTRING_LENINT(src)); @@ -2197,7 +2411,7 @@ rb_inflate_set_dictionary(VALUE obj, VALUE dic) #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; @@ -2221,7 +2435,6 @@ struct gzfile { rb_encoding *enc2; rb_econv_t *ec; VALUE ecopts; - char *cbuf; VALUE path; }; #define GZFILE_CBUF_CAPA 10 @@ -2229,12 +2442,22 @@ struct gzfile { #define GZFILE_FLAG_SYNC ZSTREAM_FLAG_UNUSED #define GZFILE_FLAG_HEADER_FINISHED (ZSTREAM_FLAG_UNUSED << 1) #define GZFILE_FLAG_FOOTER_FINISHED (ZSTREAM_FLAG_UNUSED << 2) +#define GZFILE_FLAG_MTIME_IS_SET (ZSTREAM_FLAG_UNUSED << 3) #define GZFILE_IS_FINISHED(gz) \ - (ZSTREAM_IS_FINISHED(&(gz)->z) && (gz)->z.buf_filled == 0) + (ZSTREAM_IS_FINISHED(&(gz)->z) && ZSTREAM_BUF_FILLED(&(gz)->z) == 0) #define GZFILE_READ_SIZE 2048 +enum { read_raw_arg_len, read_raw_arg_buf, read_raw_arg__count}; +struct read_raw_arg { + VALUE io; + const VALUE argv[read_raw_arg__count]; /* for rb_funcallv */ +}; + +#define read_raw_arg_argc(ra) \ + ((int)read_raw_arg__count - NIL_P((ra)->argv[read_raw_arg__count - 1])) +#define read_raw_arg_init(io, len, buf) { io, { len, buf } } static void gzfile_mark(void *p) @@ -2261,22 +2484,13 @@ gzfile_free(void *p) } zstream_finalize(z); } - if (gz->cbuf) { - xfree(gz->cbuf); - } xfree(gz); } static size_t gzfile_memsize(const void *p) { - const struct gzfile *gz = p; - size_t size = sizeof(struct gzfile); - - if (gz->cbuf) - size += GZFILE_CBUF_CAPA; - - return size; + return sizeof(struct gzfile); } static const rb_data_type_t gzfile_data_type = { @@ -2285,16 +2499,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; @@ -2312,9 +2519,17 @@ gzfile_new(klass, funcs, endfunc) gz->ec = NULL; gz->ecflags = 0; 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; } @@ -2355,9 +2570,8 @@ gzfile_write_raw(struct gzfile *gz) { VALUE str; - if (gz->z.buf_filled > 0) { + if (ZSTREAM_BUF_FILLED(&gz->z) > 0) { str = zstream_detach_buffer(&gz->z); - OBJ_TAINT(str); /* for safe */ rb_funcall(gz->io, id_write, 1, str); if ((gz->z.flags & GZFILE_FLAG_SYNC) && rb_respond_to(gz->io, id_flush)) @@ -2368,21 +2582,23 @@ gzfile_write_raw(struct gzfile *gz) static VALUE gzfile_read_raw_partial(VALUE arg) { - struct gzfile *gz = (struct gzfile*)arg; + struct read_raw_arg *ra = (struct read_raw_arg *)arg; VALUE str; + int argc = read_raw_arg_argc(ra); - str = rb_funcall(gz->io, id_readpartial, 1, INT2FIX(GZFILE_READ_SIZE)); + str = rb_funcallv(ra->io, id_readpartial, argc, ra->argv); Check_Type(str, T_STRING); return str; } static VALUE -gzfile_read_raw_rescue(VALUE arg) +gzfile_read_raw_rescue(VALUE arg, VALUE _) { - struct gzfile *gz = (struct gzfile*)arg; + struct read_raw_arg *ra = (struct read_raw_arg *)arg; VALUE str = Qnil; if (rb_obj_is_kind_of(rb_errinfo(), rb_eNoMethodError)) { - str = rb_funcall(gz->io, id_read, 1, INT2FIX(GZFILE_READ_SIZE)); + int argc = read_raw_arg_argc(ra); + str = rb_funcallv(ra->io, id_read, argc, ra->argv); if (!NIL_P(str)) { Check_Type(str, T_STRING); } @@ -2391,20 +2607,27 @@ gzfile_read_raw_rescue(VALUE arg) } static VALUE -gzfile_read_raw(struct gzfile *gz) +gzfile_read_raw(struct gzfile *gz, VALUE outbuf) { - return rb_rescue2(gzfile_read_raw_partial, (VALUE)gz, - gzfile_read_raw_rescue, (VALUE)gz, + struct read_raw_arg ra = + read_raw_arg_init(gz->io, INT2FIX(GZFILE_READ_SIZE), outbuf); + + return rb_rescue2(gzfile_read_raw_partial, (VALUE)&ra, + gzfile_read_raw_rescue, (VALUE)&ra, rb_eEOFError, rb_eNoMethodError, (VALUE)0); } static int -gzfile_read_raw_ensure(struct gzfile *gz, long size) +gzfile_read_raw_ensure(struct gzfile *gz, long size, VALUE outbuf) { VALUE str; + if (gz->io == Qundef) { /* Zlib.gunzip */ + if (NIL_P(gz->z.input) || 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); + str = gzfile_read_raw(gz, outbuf); if (NIL_P(str)) return 0; zstream_append_input2(&gz->z, str); } @@ -2421,7 +2644,7 @@ gzfile_read_raw_until_zero(struct gzfile *gz, long offset) p = memchr(RSTRING_PTR(gz->z.input) + offset, '\0', RSTRING_LEN(gz->z.input) - offset); if (p) break; - str = gzfile_read_raw(gz); + str = gzfile_read_raw(gz, Qnil); if (NIL_P(str)) { rb_raise(cGzError, "unexpected end of file"); } @@ -2502,7 +2725,7 @@ gzfile_make_header(struct gzfile *gz) if (!NIL_P(gz->comment)) { flags |= GZ_FLAG_COMMENT; } - if (gz->mtime == 0) { + if (!(gz->z.flags & GZFILE_FLAG_MTIME_IS_SET)) { gz->mtime = time(0); } @@ -2546,13 +2769,14 @@ gzfile_make_footer(struct gzfile *gz) } static void -gzfile_read_header(struct gzfile *gz) +gzfile_read_header(struct gzfile *gz, VALUE outbuf) { const unsigned char *head; long len; char flags, *p; - if (!gzfile_read_raw_ensure(gz, 10)) { /* 10 is the size of gzip header */ + /* 10 is the size of gzip header */ + if (!gzfile_read_raw_ensure(gz, 10, outbuf)) { gzfile_raise(gz, cGzError, "not in gzip format"); } @@ -2591,33 +2815,31 @@ gzfile_read_header(struct gzfile *gz) zstream_discard_input(&gz->z, 10); if (flags & GZ_FLAG_EXTRA) { - if (!gzfile_read_raw_ensure(gz, 2)) { + if (!gzfile_read_raw_ensure(gz, 2, outbuf)) { rb_raise(cGzError, "unexpected end of file"); } len = gzfile_get16((Bytef*)RSTRING_PTR(gz->z.input)); - if (!gzfile_read_raw_ensure(gz, 2 + len)) { + if (!gzfile_read_raw_ensure(gz, 2 + len, outbuf)) { rb_raise(cGzError, "unexpected end of file"); } zstream_discard_input(&gz->z, 2 + len); } if (flags & GZ_FLAG_ORIG_NAME) { - if (!gzfile_read_raw_ensure(gz, 1)) { + if (!gzfile_read_raw_ensure(gz, 1, outbuf)) { rb_raise(cGzError, "unexpected end of file"); } p = gzfile_read_raw_until_zero(gz, 0); len = p - RSTRING_PTR(gz->z.input); gz->orig_name = rb_str_new(RSTRING_PTR(gz->z.input), len); - OBJ_TAINT(gz->orig_name); /* for safe */ zstream_discard_input(&gz->z, len + 1); } if (flags & GZ_FLAG_COMMENT) { - if (!gzfile_read_raw_ensure(gz, 1)) { + if (!gzfile_read_raw_ensure(gz, 1, outbuf)) { rb_raise(cGzError, "unexpected end of file"); } p = gzfile_read_raw_until_zero(gz, 0); len = p - RSTRING_PTR(gz->z.input); gz->comment = rb_str_new(RSTRING_PTR(gz->z.input), len); - OBJ_TAINT(gz->comment); /* for safe */ zstream_discard_input(&gz->z, len + 1); } @@ -2627,13 +2849,14 @@ gzfile_read_header(struct gzfile *gz) } static void -gzfile_check_footer(struct gzfile *gz) +gzfile_check_footer(struct gzfile *gz, VALUE outbuf) { unsigned long crc, length; gz->z.flags |= GZFILE_FLAG_FOOTER_FINISHED; - if (!gzfile_read_raw_ensure(gz, 8)) { /* 8 is the size of gzip footer */ + /* 8 is the size of gzip footer */ + if (!gzfile_read_raw_ensure(gz, 8, outbuf)) { gzfile_raise(gz, cNoFooter, "footer is not found"); } @@ -2667,12 +2890,12 @@ gzfile_write(struct gzfile *gz, Bytef *str, long len) } static long -gzfile_read_more(struct gzfile *gz) +gzfile_read_more(struct gzfile *gz, VALUE outbuf) { VALUE str; while (!ZSTREAM_IS_FINISHED(&gz->z)) { - str = gzfile_read_raw(gz); + str = gzfile_read_raw(gz, outbuf); if (NIL_P(str)) { if (!ZSTREAM_IS_FINISHED(&gz->z)) { rb_raise(cGzError, "unexpected end of file"); @@ -2684,9 +2907,9 @@ gzfile_read_more(struct gzfile *gz) Z_SYNC_FLUSH); RB_GC_GUARD(str); } - if (gz->z.buf_filled > 0) break; + if (ZSTREAM_BUF_FILLED(&gz->z) > 0) break; } - return gz->z.buf_filled; + return ZSTREAM_BUF_FILLED(&gz->z); } static void @@ -2697,7 +2920,7 @@ gzfile_calc_crc(struct gzfile *gz, VALUE str) } else { gz->crc = checksum_long(crc32, gz->crc, (Bytef*)RSTRING_PTR(str) + gz->ungetc, - RSTRING_LEN(str) - gz->ungetc); + RSTRING_LEN(str) - gz->ungetc); gz->ungetc = 0; } } @@ -2707,13 +2930,11 @@ gzfile_newstr(struct gzfile *gz, VALUE str) { if (!gz->enc2) { rb_enc_associate(str, gz->enc); - OBJ_TAINT(str); /* for safe */ return str; } if (gz->ec && rb_enc_dummy_p(gz->enc2)) { str = rb_econv_str_convert(gz->ec, str, ECONV_PARTIAL_INPUT); rb_enc_associate(str, gz->enc); - OBJ_TAINT(str); return str; } return rb_str_conv_enc_opts(str, gz->enc2, gz->enc, @@ -2721,33 +2942,46 @@ gzfile_newstr(struct gzfile *gz, VALUE str) } static long -gzfile_fill(struct gzfile *gz, long len) +gzfile_fill(struct gzfile *gz, long len, VALUE outbuf) { if (len < 0) rb_raise(rb_eArgError, "negative length %ld given", len); if (len == 0) return 0; - while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled < len) { - gzfile_read_more(gz); + while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) < len) { + gzfile_read_more(gz, outbuf); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz); + gzfile_check_footer(gz, outbuf); } return -1; } - return len < gz->z.buf_filled ? len : gz->z.buf_filled; + return len < ZSTREAM_BUF_FILLED(&gz->z) ? len : ZSTREAM_BUF_FILLED(&gz->z); } static VALUE -gzfile_read(struct gzfile *gz, long len) +gzfile_read(struct gzfile *gz, long len, VALUE outbuf) { VALUE dst; - len = gzfile_fill(gz, len); - if (len == 0) return rb_str_new(0, 0); - if (len < 0) return Qnil; - dst = zstream_shift_buffer(&gz->z, len); + len = gzfile_fill(gz, len, outbuf); + + if (len < 0) { + if (!NIL_P(outbuf)) + rb_str_resize(outbuf, 0); + return Qnil; + } + if (len == 0) { + if (NIL_P(outbuf)) + return rb_str_new(0, 0); + else { + rb_str_resize(outbuf, 0); + return outbuf; + } + } + + dst = zstream_shift_buffer(&gz->z, len, outbuf); if (!NIL_P(dst)) gzfile_calc_crc(gz, dst); return dst; } @@ -2760,9 +2994,6 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf) if (len < 0) rb_raise(rb_eArgError, "negative length %ld given", len); - if (!NIL_P(outbuf)) - OBJ_TAINT(outbuf); - if (len == 0) { if (NIL_P(outbuf)) return rb_str_new(0, 0); @@ -2771,50 +3002,44 @@ gzfile_readpartial(struct gzfile *gz, long len, VALUE outbuf) return outbuf; } } - while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled == 0) { - gzfile_read_more(gz); + while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) == 0) { + gzfile_read_more(gz, outbuf); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz); + gzfile_check_footer(gz, outbuf); } if (!NIL_P(outbuf)) rb_str_resize(outbuf, 0); rb_raise(rb_eEOFError, "end of file reached"); } - dst = zstream_shift_buffer(&gz->z, len); + dst = zstream_shift_buffer(&gz->z, len, outbuf); gzfile_calc_crc(gz, dst); - if (!NIL_P(outbuf)) { - rb_str_resize(outbuf, RSTRING_LEN(dst)); - memcpy(RSTRING_PTR(outbuf), RSTRING_PTR(dst), RSTRING_LEN(dst)); - RB_GC_GUARD(dst); - dst = outbuf; - } - OBJ_TAINT(dst); /* for safe */ return dst; } static VALUE -gzfile_read_all(struct gzfile *gz) +gzfile_read_all(struct gzfile *gz, VALUE dst) { - VALUE dst; - while (!ZSTREAM_IS_FINISHED(&gz->z)) { - gzfile_read_more(gz); + gzfile_read_more(gz, dst); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz); + gzfile_check_footer(gz, dst); } + if (!NIL_P(dst)) { + rb_str_resize(dst, 0); + return dst; + } return rb_str_new(0, 0); } dst = zstream_detach_buffer(&gz->z); if (NIL_P(dst)) return dst; gzfile_calc_crc(gz, dst); - OBJ_TAINT(dst); return gzfile_newstr(gz, dst); } @@ -2825,12 +3050,12 @@ gzfile_getc(struct gzfile *gz) int len; len = rb_enc_mbmaxlen(gz->enc); - while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled < len) { - gzfile_read_more(gz); + while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) < len) { + gzfile_read_more(gz, Qnil); } if (GZFILE_IS_FINISHED(gz)) { if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz); + gzfile_check_footer(gz, Qnil); } return Qnil; } @@ -2838,27 +3063,23 @@ gzfile_getc(struct gzfile *gz) if (gz->ec && rb_enc_dummy_p(gz->enc2)) { const unsigned char *ss, *sp, *se; unsigned char *ds, *dp, *de; + VALUE cbuf = rb_enc_str_new(0, GZFILE_CBUF_CAPA, gz->enc); - if (!gz->cbuf) { - gz->cbuf = ALLOC_N(char, GZFILE_CBUF_CAPA); - } ss = sp = (const unsigned char*)RSTRING_PTR(gz->z.buf); - se = sp + gz->z.buf_filled; - ds = dp = (unsigned char *)gz->cbuf; + se = sp + ZSTREAM_BUF_FILLED(&gz->z); + ds = dp = (unsigned char *)RSTRING_PTR(cbuf); de = (unsigned char *)ds + GZFILE_CBUF_CAPA; (void)rb_econv_convert(gz->ec, &sp, se, &dp, de, ECONV_PARTIAL_INPUT|ECONV_AFTER_OUTPUT); rb_econv_check_error(gz->ec); - dst = zstream_shift_buffer(&gz->z, sp - ss); + dst = zstream_shift_buffer(&gz->z, sp - ss, Qnil); gzfile_calc_crc(gz, dst); - dst = rb_str_new(gz->cbuf, dp - ds); - rb_enc_associate(dst, gz->enc); - OBJ_TAINT(dst); - return dst; + rb_str_resize(cbuf, dp - ds); + return cbuf; } else { buf = gz->z.buf; len = rb_enc_mbclen(RSTRING_PTR(buf), RSTRING_END(buf), gz->enc); - dst = gzfile_read(gz, len); + dst = gzfile_read(gz, len, Qnil); if (NIL_P(dst)) return dst; return gzfile_newstr(gz, dst); } @@ -2900,7 +3121,7 @@ gzfile_writer_end(struct gzfile *gz) if (ZSTREAM_IS_CLOSING(&gz->z)) return; gz->z.flags |= ZSTREAM_FLAG_CLOSING; - rb_ensure(gzfile_writer_end_run, (VALUE)gz, zstream_end, (VALUE)&gz->z); + rb_ensure(gzfile_writer_end_run, (VALUE)gz, zstream_ensure_end, (VALUE)&gz->z); } static VALUE @@ -2910,7 +3131,7 @@ gzfile_reader_end_run(VALUE arg) if (GZFILE_IS_FINISHED(gz) && !(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz); + gzfile_check_footer(gz, Qnil); } return Qnil; @@ -2922,7 +3143,7 @@ gzfile_reader_end(struct gzfile *gz) if (ZSTREAM_IS_CLOSING(&gz->z)) return; gz->z.flags |= ZSTREAM_FLAG_CLOSING; - rb_ensure(gzfile_reader_end_run, (VALUE)gz, zstream_end, (VALUE)&gz->z); + rb_ensure(gzfile_reader_end_run, (VALUE)gz, zstream_ensure_end, (VALUE)&gz->z); } static void @@ -2947,12 +3168,11 @@ gzfile_reader_get_unused(struct gzfile *gz) if (!ZSTREAM_IS_READY(&gz->z)) return Qnil; if (!GZFILE_IS_FINISHED(gz)) return Qnil; if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) { - gzfile_check_footer(gz); + gzfile_check_footer(gz, Qnil); } if (NIL_P(gz->z.input)) return Qnil; str = rb_str_resurrect(gz->z.input); - OBJ_TAINT(str); /* for safe */ return str; } @@ -3019,7 +3239,7 @@ static VALUE new_wrap(VALUE tmp) { new_wrap_arg_t *arg = (new_wrap_arg_t *)tmp; - return rb_class_new_instance(arg->argc, arg->argv, arg->klass); + return rb_class_new_instance_kw(arg->argc, arg->argv, arg->klass, RB_PASS_CALLED_KEYWORDS); } static VALUE @@ -3052,7 +3272,7 @@ gzfile_wrap(int argc, VALUE *argv, VALUE klass, int close_io_on_error) } } else { - obj = rb_class_new_instance(argc, argv, klass); + obj = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS); } if (rb_block_given_p()) { @@ -3169,7 +3389,6 @@ rb_gzfile_orig_name(VALUE obj) if (!NIL_P(str)) { str = rb_str_dup(str); } - OBJ_TAINT(str); /* for safe */ return str; } @@ -3186,7 +3405,6 @@ rb_gzfile_comment(VALUE obj) if (!NIL_P(str)) { str = rb_str_dup(str); } - OBJ_TAINT(str); /* for safe */ return str; } @@ -3218,7 +3436,7 @@ rb_gzfile_set_lineno(VALUE obj, VALUE lineno) * Document-method: Zlib::GzipWriter#mtime= * * Specify the modification time (+mtime+) in the gzip header. - * Using a Fixnum or Integer. + * Using an Integer. * * Setting the mtime in the gzip header does not effect the * mtime of the file generated. Different utilities that @@ -3245,6 +3463,7 @@ rb_gzfile_set_mtime(VALUE obj, VALUE mtime) val = rb_Integer(mtime); gz->mtime = NUM2UINT(val); + gz->z.flags |= GZFILE_FLAG_MTIME_IS_SET; return mtime; } @@ -3359,6 +3578,9 @@ static VALUE rb_gzfile_eof_p(VALUE obj) { struct gzfile *gz = get_gzfile(obj); + while (!ZSTREAM_IS_FINISHED(&gz->z) && ZSTREAM_BUF_FILLED(&gz->z) == 0) { + gzfile_read_more(gz, Qnil); + } return GZFILE_IS_FINISHED(gz) ? Qtrue : Qfalse; } @@ -3417,7 +3639,14 @@ static VALUE rb_gzfile_total_out(VALUE obj) { struct gzfile *gz = get_gzfile(obj); - return rb_uint2inum(gz->z.stream.total_out - gz->z.buf_filled); + uLong total_out = gz->z.stream.total_out; + long buf_filled = ZSTREAM_BUF_FILLED(&gz->z); + + if (total_out >= (uLong)buf_filled) { + return rb_uint2inum(total_out - buf_filled); + } else { + return LONG2FIX(-(buf_filled - (long)total_out)); + } } /* @@ -3436,6 +3665,16 @@ rb_gzfile_path(VALUE obj) return gz->path; } +static VALUE +gzfile_initialize_path_partial(VALUE obj) +{ + struct gzfile* gz; + TypedData_Get_Struct(obj, struct gzfile, &gzfile_data_type, gz); + gz->path = rb_funcall(gz->io, id_path, 0); + rb_define_singleton_method(obj, "path", rb_gzfile_path, 0); + return Qnil; +} + static void rb_gzfile_ecopts(struct gzfile *gz, VALUE opts) { @@ -3544,8 +3783,8 @@ rb_gzwriter_initialize(int argc, VALUE *argv, VALUE obj) rb_gzfile_ecopts(gz, opt); if (rb_respond_to(io, id_path)) { - gz->path = rb_funcall(gz->io, id_path, 0); - rb_define_singleton_method(obj, "path", rb_gzfile_path, 0); + /* File#path may raise IOError in case when a path is unavailable */ + rb_rescue2(gzfile_initialize_path_partial, obj, NULL, Qnil, rb_eIOError, (VALUE)0); } return obj; @@ -3583,18 +3822,23 @@ rb_gzwriter_flush(int argc, VALUE *argv, VALUE obj) * Same as IO. */ static VALUE -rb_gzwriter_write(VALUE obj, VALUE str) +rb_gzwriter_write(int argc, VALUE *argv, VALUE obj) { struct gzfile *gz = get_gzfile(obj); - - if (!RB_TYPE_P(str, T_STRING)) - str = rb_obj_as_string(str); - if (gz->enc2 && gz->enc2 != rb_ascii8bit_encoding()) { - str = rb_str_conv_enc(str, rb_enc_get(str), gz->enc2); + size_t total = 0; + + while (argc-- > 0) { + VALUE str = *argv++; + if (!RB_TYPE_P(str, T_STRING)) + str = rb_obj_as_string(str); + if (gz->enc2 && gz->enc2 != rb_ascii8bit_encoding()) { + str = rb_str_conv_enc(str, rb_enc_get(str), gz->enc2); + } + gzfile_write(gz, (Bytef*)RSTRING_PTR(str), RSTRING_LEN(str)); + total += RSTRING_LEN(str); + RB_GC_GUARD(str); } - gzfile_write(gz, (Bytef*)RSTRING_PTR(str), RSTRING_LEN(str)); - RB_GC_GUARD(str); - return INT2FIX(RSTRING_LEN(str)); + return SIZET2NUM(total); } /* @@ -3710,6 +3954,60 @@ rb_gzreader_s_open(int argc, VALUE *argv, VALUE klass) } /* + * 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), Qnil); + 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: @@ -3743,12 +4041,12 @@ rb_gzreader_initialize(int argc, VALUE *argv, VALUE obj) } gz->io = io; ZSTREAM_READY(&gz->z); - gzfile_read_header(gz); + gzfile_read_header(gz, Qnil); rb_gzfile_ecopts(gz, opt); if (rb_respond_to(io, id_path)) { - gz->path = rb_funcall(gz->io, id_path, 0); - rb_define_singleton_method(obj, "path", rb_gzfile_path, 0); + /* File#path may raise IOError in case when a path is unavailable */ + rb_rescue2(gzfile_initialize_path_partial, obj, NULL, Qnil, rb_eIOError, (VALUE)0); } return obj; @@ -3791,19 +4089,19 @@ static VALUE rb_gzreader_read(int argc, VALUE *argv, VALUE obj) { struct gzfile *gz = get_gzfile(obj); - VALUE vlen; + VALUE vlen, outbuf; long len; - rb_scan_args(argc, argv, "01", &vlen); + rb_scan_args(argc, argv, "02", &vlen, &outbuf); if (NIL_P(vlen)) { - return gzfile_read_all(gz); + return gzfile_read_all(gz, outbuf); } len = NUM2INT(vlen); if (len < 0) { rb_raise(rb_eArgError, "negative length %ld given", len); } - return gzfile_read(gz, len); + return gzfile_read(gz, len, outbuf); } /* @@ -3812,7 +4110,7 @@ rb_gzreader_read(int argc, VALUE *argv, VALUE obj) * call-seq: * gzipreader.readpartial(maxlen [, outbuf]) => string, outbuf * - * Reads at most <i>maxlen</i> bytes from the gziped stream but + * Reads at most <i>maxlen</i> bytes from the gzipped stream but * it blocks only if <em>gzipreader</em> has no data immediately available. * If the optional <i>outbuf</i> argument is present, * it must reference a String, which will receive the data. @@ -3876,7 +4174,7 @@ rb_gzreader_getbyte(VALUE obj) struct gzfile *gz = get_gzfile(obj); VALUE dst; - dst = gzfile_read(gz, 1); + dst = gzfile_read(gz, 1, Qnil); if (!NIL_P(dst)) { dst = INT2FIX((unsigned int)(RSTRING_PTR(dst)[0]) & 0xff); } @@ -3936,20 +4234,6 @@ rb_gzreader_each_byte(VALUE obj) } /* - * 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. @@ -3991,27 +4275,28 @@ gzreader_skip_linebreaks(struct gzfile *gz) char *p; int n; - while (gz->z.buf_filled == 0) { + while (ZSTREAM_BUF_FILLED(&gz->z) == 0) { if (GZFILE_IS_FINISHED(gz)) return; - gzfile_read_more(gz); + gzfile_read_more(gz, Qnil); } n = 0; p = RSTRING_PTR(gz->z.buf); while (n++, *(p++) == '\n') { - if (n >= gz->z.buf_filled) { + if (n >= ZSTREAM_BUF_FILLED(&gz->z)) { str = zstream_detach_buffer(&gz->z); + ASSUME(!NIL_P(str)); gzfile_calc_crc(gz, str); - while (gz->z.buf_filled == 0) { + while (ZSTREAM_BUF_FILLED(&gz->z) == 0) { if (GZFILE_IS_FINISHED(gz)) return; - gzfile_read_more(gz); + gzfile_read_more(gz, Qnil); } n = 0; p = RSTRING_PTR(gz->z.buf); } } - str = zstream_shift_buffer(&gz->z, n - 1); + str = zstream_shift_buffer(&gz->z, n - 1, Qnil); gzfile_calc_crc(gz, str); } @@ -4026,18 +4311,18 @@ static long gzreader_charboundary(struct gzfile *gz, long n) { char *s = RSTRING_PTR(gz->z.buf); - char *e = s + gz->z.buf_filled; - char *p = rb_enc_left_char_head(s, s + n, e, gz->enc); + char *e = s + ZSTREAM_BUF_FILLED(&gz->z); + char *p = rb_enc_left_char_head(s, s + n - 1, e, gz->enc); long l = p - s; if (l < n) { - n = rb_enc_precise_mbclen(p, e, gz->enc); - if (MBCLEN_NEEDMORE_P(n)) { - if ((l = gzfile_fill(gz, l + MBCLEN_NEEDMORE_LEN(n))) > 0) { + int n_bytes = rb_enc_precise_mbclen(p, e, gz->enc); + if (MBCLEN_NEEDMORE_P(n_bytes)) { + if ((l = gzfile_fill(gz, n + MBCLEN_NEEDMORE_LEN(n_bytes), Qnil)) > 0) { return l; } } - else if (MBCLEN_CHARFOUND_P(n)) { - return l + MBCLEN_CHARFOUND_LEN(n); + else if (MBCLEN_CHARFOUND_P(n_bytes)) { + return l + MBCLEN_CHARFOUND_LEN(n_bytes); } } return n; @@ -4084,10 +4369,10 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) if (NIL_P(rs)) { if (limit < 0) { - dst = gzfile_read_all(gz); + dst = gzfile_read_all(gz, Qnil); if (RSTRING_LEN(dst) == 0) return Qnil; } - else if ((n = gzfile_fill(gz, limit)) <= 0) { + else if ((n = gzfile_fill(gz, limit, Qnil)) <= 0) { return Qnil; } else { @@ -4097,7 +4382,7 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) else { n = limit; } - dst = zstream_shift_buffer(&gz->z, n); + dst = zstream_shift_buffer(&gz->z, n, Qnil); if (NIL_P(dst)) return dst; gzfile_calc_crc(gz, dst); dst = gzfile_newstr(gz, dst); @@ -4121,25 +4406,25 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) gzreader_skip_linebreaks(gz); } - while (gz->z.buf_filled < rslen) { + while (ZSTREAM_BUF_FILLED(&gz->z) < rslen) { if (ZSTREAM_IS_FINISHED(&gz->z)) { - if (gz->z.buf_filled > 0) gz->lineno++; - return gzfile_read(gz, rslen); + if (ZSTREAM_BUF_FILLED(&gz->z) > 0) gz->lineno++; + return gzfile_read(gz, rslen, Qnil); } - gzfile_read_more(gz); + gzfile_read_more(gz, Qnil); } p = RSTRING_PTR(gz->z.buf); n = rslen; for (;;) { long filled; - if (n > gz->z.buf_filled) { + if (n > ZSTREAM_BUF_FILLED(&gz->z)) { if (ZSTREAM_IS_FINISHED(&gz->z)) break; - gzfile_read_more(gz); + gzfile_read_more(gz, Qnil); p = RSTRING_PTR(gz->z.buf) + n - rslen; } if (!rspara) rscheck(rsptr, rslen, rs); - filled = gz->z.buf_filled; + filled = ZSTREAM_BUF_FILLED(&gz->z); if (limit > 0 && filled >= limit) { filled = limit; } @@ -4156,12 +4441,12 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) p++, n++; } } - if (maxlen > 1 && n == limit && (gz->z.buf_filled > n || !ZSTREAM_IS_FINISHED(&gz->z))) { + if (maxlen > 1 && n == limit && (ZSTREAM_BUF_FILLED(&gz->z) > n || !ZSTREAM_IS_FINISHED(&gz->z))) { n = gzreader_charboundary(gz, n); } gz->lineno++; - dst = gzfile_read(gz, n); + dst = gzfile_read(gz, n, Qnil); if (NIL_P(dst)) return dst; if (rspara) { gzreader_skip_linebreaks(gz); @@ -4175,6 +4460,8 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) * 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) @@ -4222,20 +4509,6 @@ rb_gzreader_each(int argc, VALUE *argv, VALUE 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. @@ -4262,11 +4535,187 @@ rb_gzreader_external_encoding(VALUE self) return rb_enc_from_encoding(get_gzfile(self)->enc); } +static VALUE +zlib_gzip_end_rescue(VALUE arg) +{ + struct gzfile *gz = (struct gzfile *)arg; + gz->end(gz); + return Qnil; +} + +static VALUE +zlib_gzip_ensure(VALUE arg) +{ + return rb_rescue(zlib_gzip_end_rescue, arg, NULL, Qnil); +} + +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); +} + +static ID id_level, id_strategy; +static VALUE zlib_gzip_run(VALUE arg); + +/* + * call-seq: + * Zlib.gzip(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 + * sio.binmode + * gz = Zlib::GzipWriter.new(sio, level, strategy) + * gz.write(string) + * gz.close + * sio.string + * end + * + * See also Zlib.gunzip + * + */ +static VALUE +zlib_s_gzip(int argc, VALUE *argv, VALUE klass) +{ + struct gzfile gz0; + struct gzfile *gz = &gz0; + int err; + VALUE src, opts, level=Qnil, strategy=Qnil, args[2]; + + if (OPTHASH_GIVEN_P(opts)) { + ID keyword_ids[2]; + VALUE kwargs[2]; + keyword_ids[0] = id_level; + keyword_ids[1] = id_strategy; + rb_get_kwargs(opts, keyword_ids, 0, 2, kwargs); + if (kwargs[0] != Qundef) { + level = kwargs[0]; + } + if (kwargs[1] != Qundef) { + strategy = kwargs[1]; + } + } + rb_scan_args(argc, argv, "10", &src); + 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) { + zlib_gzip_end(gz); + raise_zlib_error(err, gz->z.stream.msg); + } + ZSTREAM_READY(&gz->z); + args[0] = (VALUE)gz; + args[1] = src; + return rb_ensure(zlib_gzip_run, (VALUE)args, zlib_gzip_ensure, (VALUE)gz); +} + +static VALUE +zlib_gzip_run(VALUE arg) +{ + VALUE *args = (VALUE *)arg; + struct gzfile *gz = (struct gzfile *)args[0]; + VALUE src = args[1]; + long len; + + 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; + zstream_end(&gz->z); +} + +static VALUE zlib_gunzip_run(VALUE arg); + +/* + * 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) + * gz = Zlib::GzipReader.new(sio, encoding: Encoding::ASCII_8BIT) + * gz.read + * ensure + * gz&.close + * end + * + * See also Zlib.gzip + */ +static VALUE +zlib_gunzip(VALUE klass, VALUE src) +{ + struct gzfile gz0; + struct gzfile *gz = &gz0; + int err; + + 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); + return rb_ensure(zlib_gunzip_run, (VALUE)gz, zlib_gzip_ensure, (VALUE)gz); +} + +static VALUE +zlib_gunzip_run(VALUE arg) +{ + struct gzfile *gz = (struct gzfile *)arg; + VALUE dst; + + gzfile_read_header(gz, Qnil); + dst = zstream_detach_buffer(&gz->z); + ASSUME(!NIL_P(dst)); + 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, Qnil); + return dst; +} + #endif /* GZIP_SUPPORT */ void Init_zlib(void) { +#ifdef 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; @@ -4284,6 +4733,7 @@ Init_zlib(void) cMemError = rb_define_class_under(mZlib, "MemError", cZError); cBufError = rb_define_class_under(mZlib, "BufError", cZError); cVersionError = rb_define_class_under(mZlib, "VersionError", cZError); + cInProgressError = rb_define_class_under(mZlib, "InProgressError", cZError); rb_define_module_function(mZlib, "zlib_version", rb_zlib_version, 0); rb_define_module_function(mZlib, "adler32", rb_zlib_adler32, -1); @@ -4360,7 +4810,7 @@ Init_zlib(void) 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); @@ -4501,7 +4951,7 @@ Init_zlib(void) rb_define_alloc_func(cGzipWriter, rb_gzwriter_s_allocate); rb_define_method(cGzipWriter, "initialize", rb_gzwriter_initialize,-1); rb_define_method(cGzipWriter, "flush", rb_gzwriter_flush, -1); - rb_define_method(cGzipWriter, "write", rb_gzwriter_write, 1); + rb_define_method(cGzipWriter, "write", rb_gzwriter_write, -1); rb_define_method(cGzipWriter, "putc", rb_gzwriter_putc, 1); rb_define_method(cGzipWriter, "<<", rb_gzwriter_addstr, 1); rb_define_method(cGzipWriter, "printf", rb_gzwriter_printf, -1); @@ -4509,6 +4959,7 @@ Init_zlib(void) 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); @@ -4521,17 +4972,18 @@ Init_zlib(void) 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); + /* The OS code of current host */ rb_define_const(mZlib, "OS_CODE", INT2FIX(OS_CODE)); /* OS code for MSDOS hosts */ @@ -4565,6 +5017,9 @@ Init_zlib(void) /* 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 */ } @@ -4586,6 +5041,7 @@ Init_zlib(void) * - Zlib::MemError * - Zlib::BufError * - Zlib::VersionError + * - Zlib::InProgressError * */ @@ -4661,6 +5117,20 @@ Init_zlib(void) */ /* + * Document-class: Zlib::InProgressError + * + * Subclass of Zlib::Error. This error is raised when the zlib + * stream is currently in progress. + * + * For example: + * + * inflater = Zlib::Inflate.new + * inflater.inflate(compressed) do + * inflater.inflate(compressed) # Raises Zlib::InProgressError + * end + */ + +/* * Document-class: Zlib::GzipFile::Error * * Base class of errors that occur when processing GZIP files. @@ -4685,5 +5155,3 @@ Init_zlib(void) * Raised when the data length recorded in the gzip file footer is not equivalent * to the length of the actual uncompressed data. */ - - diff --git a/ext/zlib/zlib.gemspec b/ext/zlib/zlib.gemspec new file mode 100644 index 0000000000..ba7114476f --- /dev/null +++ b/ext/zlib/zlib.gemspec @@ -0,0 +1,31 @@ +# coding: utf-8 +# frozen_string_literal: true + +source_version = ["", "ext/zlib/"].find do |dir| + begin + break File.open(File.join(__dir__, "#{dir}zlib.c")) {|f| + f.gets("\n#define RUBY_ZLIB_VERSION ") + f.gets[/\s*"(.+)"/, 1] + } + rescue Errno::ENOENT + end +end + +Gem::Specification.new do |spec| + spec.name = "zlib" + spec.version = source_version + spec.authors = ["Yukihiro Matsumoto", "UENO Katsuhiro"] + spec.email = ["matz@ruby-lang.org", nil] + + spec.summary = %q{Ruby interface for the zlib compression/decompression library} + spec.description = %q{Ruby interface for the zlib compression/decompression library} + spec.homepage = "https://github.com/ruby/zlib" + spec.licenses = ["Ruby", "BSD-2-Clause"] + + spec.files = ["COPYING", "BSDL", "README.md", "ext/zlib/extconf.rb", "ext/zlib/zlib.c", "zlib.gemspec"] + spec.bindir = "exe" + spec.executables = [] + spec.require_paths = ["lib"] + spec.extensions = "ext/zlib/extconf.rb" + spec.required_ruby_version = ">= 2.7.0" +end |
