Skip to content
Browse files

Backport new zlib impl with JZlib

Just for performance comparison with old implementation.
  • Loading branch information...
1 parent 78a5fa7 commit 3bcb9ad17fad12561f2143f71d0f1c1199f308a6 @nahi nahi committed Oct 11, 2011
View
1 .classpath
@@ -38,5 +38,6 @@
<classpathentry kind="lib" path="build_lib/jnr-netdb.jar"/>
<classpathentry kind="lib" path="build_lib/org.osgi.core-4.2.0.jar"/>
<classpathentry kind="lib" path="build_lib/snakeyaml-1.9.jar"/>
+ <classpathentry kind="lib" path="build_lib/jzlib-1.1.0.jar"/>
<classpathentry kind="output" path="build.eclipse"/>
</classpath>
View
62 bench/bench_zlib.rb
@@ -0,0 +1,62 @@
+require 'benchmark'
+require 'zlib'
+require 'stringio'
+
+FILES = Dir.glob(File.expand_path('*.rb', File.dirname(__FILE__)))
+
+Benchmark.bmbm do |bm|
+ bm.report('Zlib::Deflate') do
+ z = Zlib::Deflate.new
+ 300.times do
+ FILES.each do |file|
+ z << File.read(file)
+ end
+ z.flush
+ end
+ z.finish
+ end
+
+ bm.report('Zlib::Inflate') do
+ # prepare
+ z = Zlib::Deflate.new
+ FILES.each do |file|
+ z << File.read(file)
+ end
+ src = z.finish
+
+ 3000.times do
+ z = Zlib::Inflate.new
+ z << src
+ z.finish
+ end
+ end
+
+ bm.report('Zlib::GzipWriter') do
+ s = StringIO.new
+ Zlib::GzipWriter.wrap(s) do |gz|
+ 300.times do
+ FILES.each do |file|
+ gz.write File.read(file)
+ end
+ s.truncate(0)
+ end
+ end
+ end
+
+ bm.report('Zlib::GzipReader') do
+ # prepare
+ s = StringIO.new
+ Zlib::GzipWriter.wrap(s) do |gz|
+ FILES.each do |file|
+ gz.write File.read(file)
+ end
+ end
+ src = s.string
+
+ 3000.times do
+ Zlib::GzipReader.wrap(StringIO.new(src)) do |gz|
+ gz.read
+ end
+ end
+ end
+end
View
3 build.xml
@@ -369,6 +369,7 @@
<zipfileset src="${build.lib.dir}/yecht.jar"/>
<zipfileset src="${build.lib.dir}/yydebug.jar"/>
<zipfileset src="${build.lib.dir}/nailgun-0.7.1.jar"/>
+ <zipfileset src="${build.lib.dir}/jzlib-1.1.0.jar"/>
<metainf dir="spi">
<include name="services/**"/>
</metainf>
@@ -469,6 +470,7 @@
<zipfileset src="${build.lib.dir}/yecht.jar"/>
<zipfileset src="${build.lib.dir}/yydebug.jar"/>
<zipfileset src="${build.lib.dir}/nailgun-0.7.1.jar"/>
+ <zipfileset src="${build.lib.dir}/jzlib-1.1.0.jar"/>
<metainf dir="spi">
<include name="services/**"/>
</metainf>
@@ -627,6 +629,7 @@
<zipfileset src="${build.lib.dir}/yecht.jar"/>
<zipfileset src="${build.lib.dir}/yydebug.jar"/>
<zipfileset src="${build.lib.dir}/nailgun-0.7.1.jar"/>
+ <zipfileset src="${build.lib.dir}/jzlib-1.1.0.jar"/>
<metainf dir="spi">
<include name="services/**"/>
</metainf>
View
BIN build_lib/jzlib-1.1.0.jar
Binary file not shown.
View
1 spec/tags/1.8/ruby/library/zlib/deflate/params_tags.txt
@@ -1 +0,0 @@
-fails(JRUBY-3775):Zlib::Deflate#params changes the deflate parameters
View
1 spec/tags/1.8/ruby/library/zlib/gzipwriter/mtime_tags.txt
@@ -0,0 +1 @@
+fails:Zlib::GzipWriter#mtime= raises if the header was written
View
1 spec/tags/1.9/ruby/library/zlib/gzipwriter/mtime_tags.txt
@@ -0,0 +1 @@
+fails:Zlib::GzipWriter#mtime= raises if the header was written
View
1,096 src/org/jruby/RubyZlib.java
@@ -33,23 +33,28 @@
package org.jruby;
import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
-import java.util.zip.CRC32;
-import java.util.zip.DataFormatException;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-
import org.jcodings.Encoding;
import org.joda.time.DateTime;
+import org.jruby.Ruby;
+import org.jruby.RubyBasicObject;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyIO;
+import org.jruby.RubyKernel;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.RubyStringIO;
+import org.jruby.RubyTime;
+import org.jruby.RubyBoolean;
+import org.jruby.RubyException;
import org.jruby.anno.FrameField;
import org.jruby.anno.JRubyClass;
@@ -77,8 +82,9 @@
import org.jruby.util.TypeConverter;
import org.jruby.util.io.Stream;
+import com.jcraft.jzlib.JZlib;
+
import static org.jruby.CompatVersion.*;
-import org.jruby.ext.zlib.Util;
@JRubyModule(name="Zlib")
public class RubyZlib {
@@ -117,12 +123,12 @@ public static RubyModule createZlibModule(Ruby runtime) {
cGzFile.defineClassUnder("NoFooter", cGzError, cGzError.getAllocator());
cGzFile.defineClassUnder("LengthError", cGzError, cGzError.getAllocator());
- RubyClass cGzReader = mZlib.defineClassUnder("GzipReader", cGzFile, RubyGzipReader.GZIPREADER_ALLOCATOR);
+ RubyClass cGzReader = mZlib.defineClassUnder("GzipReader", cGzFile, JZlibRubyGzipReader.GZIPREADER_ALLOCATOR);
cGzReader.includeModule(runtime.getEnumerable());
- cGzReader.defineAnnotatedMethods(RubyGzipReader.class);
+ cGzReader.defineAnnotatedMethods(JZlibRubyGzipReader.class);
- RubyClass cGzWriter = mZlib.defineClassUnder("GzipWriter", cGzFile, RubyGzipWriter.GZIPWRITER_ALLOCATOR);
- cGzWriter.defineAnnotatedMethods(RubyGzipWriter.class);
+ RubyClass cGzWriter = mZlib.defineClassUnder("GzipWriter", cGzFile, JZlibRubyGzipWriter.GZIPWRITER_ALLOCATOR);
+ cGzWriter.defineAnnotatedMethods(JZlibRubyGzipWriter.class);
mZlib.defineConstant("ZLIB_VERSION", runtime.newString(ZLIB_VERSION));
mZlib.defineConstant("VERSION", runtime.newString(VERSION));
@@ -151,32 +157,32 @@ public static RubyModule createZlibModule(Ruby runtime) {
mZlib.defineConstant("OS_CPM", runtime.newFixnum(OS_CPM));
mZlib.defineConstant("OS_TOPS20", runtime.newFixnum(OS_TOPS20));
- mZlib.defineConstant("DEFAULT_STRATEGY", runtime.newFixnum(Z_DEFAULT_STRATEGY));
- mZlib.defineConstant("FILTERED", runtime.newFixnum(Z_FILTERED));
- mZlib.defineConstant("HUFFMAN_ONLY", runtime.newFixnum(Z_HUFFMAN_ONLY));
+ mZlib.defineConstant("DEFAULT_STRATEGY", runtime.newFixnum(JZlib.Z_DEFAULT_STRATEGY));
+ mZlib.defineConstant("FILTERED", runtime.newFixnum(JZlib.Z_FILTERED));
+ mZlib.defineConstant("HUFFMAN_ONLY", runtime.newFixnum(JZlib.Z_HUFFMAN_ONLY));
- mZlib.defineConstant("NO_FLUSH", runtime.newFixnum(Z_NO_FLUSH));
- mZlib.defineConstant("SYNC_FLUSH", runtime.newFixnum(Z_SYNC_FLUSH));
- mZlib.defineConstant("FULL_FLUSH", runtime.newFixnum(Z_FULL_FLUSH));
- mZlib.defineConstant("FINISH", runtime.newFixnum(Z_FINISH));
+ mZlib.defineConstant("NO_FLUSH", runtime.newFixnum(JZlib.Z_NO_FLUSH));
+ mZlib.defineConstant("SYNC_FLUSH", runtime.newFixnum(JZlib.Z_SYNC_FLUSH));
+ mZlib.defineConstant("FULL_FLUSH", runtime.newFixnum(JZlib.Z_FULL_FLUSH));
+ mZlib.defineConstant("FINISH", runtime.newFixnum(JZlib.Z_FINISH));
- mZlib.defineConstant("NO_COMPRESSION", runtime.newFixnum(Z_NO_COMPRESSION));
- mZlib.defineConstant("BEST_SPEED", runtime.newFixnum(Z_BEST_SPEED));
- mZlib.defineConstant("DEFAULT_COMPRESSION", runtime.newFixnum(Z_DEFAULT_COMPRESSION));
- mZlib.defineConstant("BEST_COMPRESSION", runtime.newFixnum(Z_BEST_COMPRESSION));
+ mZlib.defineConstant("NO_COMPRESSION", runtime.newFixnum(JZlib.Z_NO_COMPRESSION));
+ mZlib.defineConstant("BEST_SPEED", runtime.newFixnum(JZlib.Z_BEST_SPEED));
+ mZlib.defineConstant("DEFAULT_COMPRESSION", runtime.newFixnum(JZlib.Z_DEFAULT_COMPRESSION));
+ mZlib.defineConstant("BEST_COMPRESSION", runtime.newFixnum(JZlib.Z_BEST_COMPRESSION));
- mZlib.defineConstant("MAX_WBITS", runtime.newFixnum(MAX_WBITS));
+ mZlib.defineConstant("MAX_WBITS", runtime.newFixnum(JZlib.MAX_WBITS));
// ZStream actually *isn't* allocatable
RubyClass cZStream = mZlib.defineClassUnder("ZStream", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
cZStream.defineAnnotatedMethods(ZStream.class);
cZStream.undefineMethod("new");
- RubyClass cInflate = mZlib.defineClassUnder("Inflate", cZStream, Inflate.INFLATE_ALLOCATOR);
- cInflate.defineAnnotatedMethods(Inflate.class);
+ RubyClass cInflate = mZlib.defineClassUnder("Inflate", cZStream, JZlibInflate.INFLATE_ALLOCATOR);
+ cInflate.defineAnnotatedMethods(JZlibInflate.class);
- RubyClass cDeflate = mZlib.defineClassUnder("Deflate", cZStream, Deflate.DEFLATE_ALLOCATOR);
- cDeflate.defineAnnotatedMethods(Deflate.class);
+ RubyClass cDeflate = mZlib.defineClassUnder("Deflate", cZStream, JZlibDeflate.DEFLATE_ALLOCATOR);
+ cDeflate.defineAnnotatedMethods(JZlibDeflate.class);
runtime.getKernel().callMethod(runtime.getCurrentContext(), "require", runtime.newString("stringio"));
@@ -202,9 +208,9 @@ public static RubyModule createZlibModule(Ruby runtime) {
@JRubyMethod(name = "zlib_version", module = true, visibility = PRIVATE)
public static IRubyObject zlib_version(IRubyObject recv) {
- RubyBasicObject res = (RubyBasicObject) ((RubyModule)recv).fastGetConstant("ZLIB_VERSION");
+ RubyBasicObject res = (RubyBasicObject) ((RubyModule)recv).getConstant("ZLIB_VERSION");
// MRI behavior, enforced by tests
- res.taint(recv.getRuntime());
+ res.setTaint(true);
return res;
}
@@ -240,6 +246,7 @@ public static IRubyObject adler32(IRubyObject recv, IRubyObject[] args) {
return recv.getRuntime().newFixnum(ext.getValue());
}
+ // TODO: com.jcraft.jzlib.CRC32 has this table...
private final static long[] crctab = new long[]{
0L, 1996959894L, 3993919788L, 2567524794L, 124634137L, 1886057615L, 3915621685L, 2657392035L, 249268274L, 2044508324L, 3772115230L, 2547177864L, 162941995L,
2125561021L, 3887607047L, 2428444049L, 498536548L, 1789927666L, 4089016648L, 2227061214L, 450548861L, 1843258603L, 4107580753L, 2211677639L, 325883990L,
@@ -273,6 +280,32 @@ public static IRubyObject crc_table(IRubyObject recv) {
return recv.getRuntime().newArray(ll);
}
+ @JRubyMethod(name = "crc32_combine", required = 3, module = true, visibility = PRIVATE)
+ public static IRubyObject crc32_combine(IRubyObject recv,
+ IRubyObject arg0,
+ IRubyObject arg1,
+ IRubyObject arg2) {
+ long crc1 = RubyNumeric.num2long(arg0);
+ long crc2 = RubyNumeric.num2long(arg1);
+ long len2 = RubyNumeric.num2long(arg2);
+
+ long crc3 = com.jcraft.jzlib.JZlib.crc32_combine(crc1, crc2, len2);
+ return recv.getRuntime().newFixnum(crc3);
+ }
+
+ @JRubyMethod(name = "adler32_combine", required = 3, module = true, visibility = PRIVATE)
+ public static IRubyObject adler32_combine(IRubyObject recv,
+ IRubyObject arg0,
+ IRubyObject arg1,
+ IRubyObject arg2) {
+ long adler1 = RubyNumeric.num2long(arg0);
+ long adler2 = RubyNumeric.num2long(arg1);
+ long len2 = RubyNumeric.num2long(arg2);
+
+ long adler3 = com.jcraft.jzlib.JZlib.adler32_combine(adler1, adler2, len2);
+ return recv.getRuntime().newFixnum(adler3);
+ }
+
@JRubyClass(name="Zlib::ZStream")
public static abstract class ZStream extends RubyObject {
protected boolean closed = false;
@@ -316,7 +349,7 @@ public IRubyObject stream_end_p() {
@JRubyMethod(name = "data_type")
public IRubyObject data_type() {
checkClosed();
- return getRuntime().fastGetModule("Zlib").fastGetConstant("UNKNOWN");
+ return getRuntime().getModule("Zlib").getConstant("UNKNOWN");
}
@JRubyMethod(name = { "closed?", "ended?"})
@@ -388,13 +421,14 @@ public IRubyObject close() {
void checkClosed() {
if (closed) {
- throw Util.newZlibError(getRuntime(), "stream is not ready");
+ throw newZlibError(getRuntime(), "stream is not ready");
}
}
+ // TODO: remove when JZlib checks the given level
static void checkLevel(Ruby runtime, int level) {
- if ((level < 0 || level > 9) && level != Deflater.DEFAULT_COMPRESSION) {
- throw Util.newStreamError(runtime, "stream error: invalid level");
+ if ((level < 0 || level > 9) && level != JZlib.Z_DEFAULT_COMPRESSION) {
+ throw newStreamError(runtime, "stream error: invalid level");
}
}
@@ -403,62 +437,63 @@ static void checkLevel(Ruby runtime, int level) {
* NOTE: deflateInit2 of zlib.c also accepts MAX_WBITS + 16(gzip compression).
* inflateInit2 also accepts MAX_WBITS + 16(gzip decompression) and MAX_WBITS + 32(automatic detection of gzip and LZ77).
*/
+ // TODO: remove when JZlib checks the given windowBits
static void checkWindowBits(Ruby runtime, int wbits, boolean forInflate) {
wbits = Math.abs(wbits);
- if ((wbits & 0xf) < MIN_WBITS) {
- throw Util.newStreamError(runtime, "stream error: invalid window bits");
+ if ((wbits & 0xf) < 8) {
+ throw newStreamError(runtime, "stream error: invalid window bits");
}
if ((wbits & 0xf) != 0xf) {
// windowBits < 15 for reducing memory is meaningless on Java platform.
runtime.getWarnings().warn("windowBits < 15 is ignored on this platform");
// continue
}
- if (forInflate && wbits > MAX_WBITS + 32) {
- throw Util.newStreamError(runtime, "stream error: invalid window bits");
- } else if (!forInflate && wbits > MAX_WBITS + 16) {
- throw Util.newStreamError(runtime, "stream error: invalid window bits");
+ if (forInflate && wbits > JZlib.MAX_WBITS + 32) {
+ throw newStreamError(runtime, "stream error: invalid window bits");
+ } else if (!forInflate && wbits > JZlib.MAX_WBITS + 16) {
+ throw newStreamError(runtime, "stream error: invalid window bits");
}
}
+ // TODO: remove when JZlib checks the given strategy
static void checkStrategy(Ruby runtime, int strategy) {
switch (strategy) {
- case Deflater.DEFAULT_STRATEGY:
- case Deflater.FILTERED:
- case Deflater.HUFFMAN_ONLY:
+ case JZlib.Z_DEFAULT_STRATEGY:
+ case JZlib.Z_FILTERED:
+ case JZlib.Z_HUFFMAN_ONLY:
break;
default:
- throw Util.newStreamError(runtime, "stream error: invalid strategy");
+ throw newStreamError(runtime, "stream error: invalid strategy");
}
}
}
@JRubyClass(name = "Zlib::Inflate", parent = "Zlib::ZStream")
- public static class Inflate extends ZStream {
+ public static class JZlibInflate extends ZStream {
public static final int BASE_SIZE = 100;
- private Inflater flater;
private int windowBits;
- private boolean readHeaderNeeded = false;
- private boolean readTrailerNeeded = false;
- private CRC32 checksum;
private ByteList collected;
private ByteList input;
+
+ private com.jcraft.jzlib.Inflater flater = null;
+
protected static final ObjectAllocator INFLATE_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new Inflate(runtime, klass);
+ return new JZlibInflate(runtime, klass);
}
};
- public Inflate(Ruby runtime, RubyClass type) {
+ public JZlibInflate(Ruby runtime, RubyClass type) {
super(runtime, type);
}
@JRubyMethod(name = "inflate", required = 1, meta = true, backtrace = true)
public static IRubyObject s_inflate(ThreadContext context, IRubyObject recv, IRubyObject string) {
RubyClass klass = (RubyClass) recv;
- Inflate inflate = (Inflate) klass.allocate();
- inflate.init(MAX_WBITS);
+ JZlibInflate inflate = (JZlibInflate) klass.allocate();
+ inflate.init(JZlib.DEF_WBITS);
IRubyObject result;
try {
@@ -472,7 +507,7 @@ public static IRubyObject s_inflate(ThreadContext context, IRubyObject recv, IRu
@JRubyMethod(name = "initialize", optional = 1, visibility = PRIVATE)
public IRubyObject _initialize(IRubyObject[] args) {
- windowBits = MAX_WBITS;
+ windowBits = JZlib.DEF_WBITS;
if (args.length > 0 && !args[0].isNil()) {
windowBits = RubyNumeric.fix2int(args[0]);
@@ -484,19 +519,8 @@ public IRubyObject _initialize(IRubyObject[] args) {
}
private void init(int windowBits) {
- boolean nowrap = false;
- if (windowBits < 0) {
- nowrap = true;
- } else if ((windowBits & 0x10) != 0) {
- nowrap = true; // gzip wrapper
- readHeaderNeeded = true;
- checksum = new CRC32();
- } else if ((windowBits & 0x20) != 0) {
- nowrap = true; // automatic detection
- readHeaderNeeded = true;
- checksum = new CRC32();
- }
- flater = new Inflater(nowrap);
+ flater = new com.jcraft.jzlib.Inflater();
+ flater.init(windowBits);
collected = new ByteList(BASE_SIZE);
input = new ByteList();
}
@@ -511,7 +535,7 @@ private RubyString flushOutput(Ruby runtime) {
if (collected.getRealSize() > 0) {
RubyString res = RubyString.newString(runtime, collected.getUnsafeBytes(),
collected.getBegin(), collected.getRealSize());
- Util.resetBuffer(collected);
+ resetBuffer(collected);
return res;
}
return RubyString.newEmptyString(runtime);
@@ -530,30 +554,7 @@ public IRubyObject append(ThreadContext context, IRubyObject arg) {
public void append(ByteList obj) {
if (!internalFinished()) {
- if (readHeaderNeeded) {
- input.append(obj);
- byte[] bytes = input.bytes();
- int size = parseHeader(bytes);
- switch (size) {
- case -1:
- // not in gzip format; reinitialize Inflater
- init(windowBits & 0xf);
- flater.setInput(obj.bytes());
- input = new ByteList(bytes, false);
- break;
- case 0:
- // buffer is short
- return;
- default:
- flater.setInput(bytes, size, bytes.length - size);
- input = new ByteList(bytes, size, bytes.length - size, false);
- break;
- }
- } else {
- byte[] bytes = obj.bytes();
- flater.setInput(bytes);
- input = new ByteList(bytes, false);
- }
+ flater.setInput(obj.bytes(), true);
} else {
input.append(obj);
}
@@ -566,20 +567,36 @@ public IRubyObject sync_point_p() {
}
public IRubyObject sync_point() {
- return getRuntime().getFalse();
+ int ret = flater.syncPoint();
+ switch(ret){
+ case 1:
+ return getRuntime().getTrue();
+ case com.jcraft.jzlib.JZlib.Z_DATA_ERROR:
+ throw newStreamError(getRuntime(), "stream error");
+ default:
+ return getRuntime().getFalse();
+ }
}
@JRubyMethod(name = "set_dictionary", required = 1, backtrace = true)
public IRubyObject set_dictionary(ThreadContext context, IRubyObject arg) {
try {
return set_dictionary(arg);
} catch (IllegalArgumentException iae) {
- throw Util.newStreamError(context.getRuntime(), "stream error: " + iae.getMessage());
+ throw newStreamError(context.getRuntime(), "stream error: " + iae.getMessage());
}
}
private IRubyObject set_dictionary(IRubyObject str) {
- flater.setDictionary(str.convertToString().getBytes());
+ byte [] tmp = str.convertToString().getBytes();
+ int ret = flater.setDictionary(tmp, tmp.length);
+ switch(ret){
+ case com.jcraft.jzlib.JZlib.Z_STREAM_ERROR:
+ throw newStreamError(getRuntime(), "stream error");
+ case com.jcraft.jzlib.JZlib.Z_DATA_ERROR:
+ throw newDataError(getRuntime(), "wrong dictionary");
+ default:
+ }
run(false);
return str;
}
@@ -604,14 +621,29 @@ public IRubyObject inflate(ThreadContext context, ByteList str) {
@JRubyMethod(name = "sync", required = 1)
public IRubyObject sync(ThreadContext context, IRubyObject string) {
- try {
- append(context, string);
- } catch (RaiseException re) {
- if (!re.getException().getMetaClass().getRealClass().getName().equals("Zlib::DataError")) {
- throw re;
+ if(flater.avail_in>0){
+ switch(flater.sync()){
+ case com.jcraft.jzlib.JZlib.Z_OK:
+ flater.setInput(string.convertToString().getByteList().bytes(),
+ true);
+ return getRuntime().getTrue();
+ case com.jcraft.jzlib.JZlib.Z_DATA_ERROR:
+ break;
+ default:
+ throw newStreamError(getRuntime(), "stream error");
}
}
- return context.getRuntime().getFalse();
+ if(string.convertToString().getByteList().length()<=0)
+ return getRuntime().getFalse();
+ flater.setInput(string.convertToString().getByteList().bytes(), true);
+ switch(flater.sync()){
+ case com.jcraft.jzlib.JZlib.Z_OK:
+ return getRuntime().getTrue();
+ case com.jcraft.jzlib.JZlib.Z_DATA_ERROR:
+ return getRuntime().getFalse();
+ default:
+ throw newStreamError(getRuntime(), "stream error");
+ }
}
private void run(boolean finish) {
@@ -621,57 +653,62 @@ private void run(boolean finish) {
while (!internalFinished() && resultLength != 0) {
// MRI behavior
- if (finish && flater.needsInput()) {
- throw Util.newBufError(runtime, "buffer error");
+ boolean needsInput = flater.avail_in<0;
+ if (finish && needsInput) {
+ throw newBufError(runtime, "buffer error");
}
- try {
- resultLength = flater.inflate(outp);
- if (flater.needsDictionary()) {
- throw Util.newDictError(runtime, "need dictionary");
- } else {
- if (input.getRealSize() > 0) {
- int remaining = flater.getRemaining();
- if (remaining > 0) {
- input.view(input.getRealSize() - remaining, remaining);
- } else {
- Util.resetBuffer(input);
- }
+ flater.setOutput(outp);
+
+ int ret = flater.inflate(com.jcraft.jzlib.JZlib.Z_NO_FLUSH);
+ switch(ret){
+ case com.jcraft.jzlib.JZlib.Z_DATA_ERROR:
+ resultLength = flater.next_out_index;
+ if(resultLength>0){
+ // error has been occurred,
+ // but some data has been inflated successfully.
+ collected.append(outp, 0, resultLength);
}
- }
- } catch (DataFormatException ex) {
- throw Util.newDataError(runtime, "data error: " + ex.getMessage());
+ throw newDataError(runtime, flater.getMessage());
+ case com.jcraft.jzlib.JZlib.Z_NEED_DICT:
+ throw newDictError(runtime, "need dictionary");
+ case com.jcraft.jzlib.JZlib.Z_STREAM_END:
+ if(flater.avail_in>0){
+ // MRI behavior: pass-through
+ input.append(flater.next_in,
+ flater.next_in_index, flater.avail_in);
+ flater.setInput("".getBytes());
+ }
+ case com.jcraft.jzlib.JZlib.Z_OK:
+ resultLength = flater.next_out_index;
+ break;
+ default:
+ resultLength = 0;
}
- if (checksum != null) {
- checksum.update(outp, 0, resultLength);
- }
collected.append(outp, 0, resultLength);
if (resultLength == outp.length) {
outp = new byte[outp.length * 2];
}
}
- // process trailer if needed
- if (internalFinished() && readTrailerNeeded) {
- if (input.getRealSize() >= 8) {
- readTrailer(input.bytes(), flater.getBytesWritten() & 0xffffffffL,
- checksum.getValue());
- input.view(8, input.getRealSize() - 8);
- } else if (finish) {
- throw Util.newBufError(runtime, "buffer error");
+ if(finish){
+ if(!internalFinished()){
+ int err = flater.inflate(com.jcraft.jzlib.JZlib.Z_FINISH);
+ if(err != com.jcraft.jzlib.JZlib.Z_OK){
+ throw newBufError(getRuntime(), "buffer error");
+ }
}
}
- if (finish) flater.end();
}
@Override
protected int internalTotalIn() {
- return flater.getTotalIn();
+ return (int)flater.total_in;
}
@Override
protected int internalTotalOut() {
- return flater.getTotalOut();
+ return (int)flater.total_out;
}
@Override
@@ -691,7 +728,7 @@ protected boolean internalFinished() {
@Override
protected long internalAdler() {
- return (checksum != null) ? checksum.getValue() : flater.getAdler() & 0xffffffffL;
+ return flater.getAdler();
}
@Override
@@ -701,7 +738,7 @@ protected IRubyObject internalFinish() {
if (internalFinished()) {
if (input.getRealSize() > 0) {
collected.append(input);
- Util.resetBuffer(input);
+ resetBuffer(input);
}
}
return flushOutput(getRuntime());
@@ -712,92 +749,68 @@ protected void internalClose() {
flater.end();
}
- private int parseHeader(byte[] bytes) {
- ByteArrayInputStream is = new ByteArrayInputStream(bytes);
- try {
- // parsed Gzip header is not used
- Util.GzipHeader header = Util.readHeader(getRuntime(), is);
- if (header == null) {
- // Not a gzip format
- return -1;
- }
- readHeaderNeeded = false;
- readTrailerNeeded = true;
- return header.length;
- } catch (RaiseException re) {
- return 0;
- }
+ @Override
+ public IRubyObject avail_in() {
+ return getRuntime().newFixnum(flater.avail_in);
}
- private void readTrailer(byte[] trailer, long bytesWritten, long checksum) {
- Ruby runtime = getRuntime();
- try {
- Util.checkTrailer(runtime, trailer, bytesWritten, checksum);
- readTrailerNeeded = false;
- } catch (RaiseException re) {
- /*
- * uglish exception conversion. zlib.c returns Z_DATA_ERROR for
- * gzip footer error so Zlib::Inflate raises DataError for any
- * footer error. Unlike Zlib::Inflate, GZipReader raises
- * NoFooter, CRCError or LengthError for footer error (ext/zlib
- * parses by itself.)
- */
- throw Util.newDataError(runtime, re.getMessage());
- }
+ private static void resetBuffer(ByteList l) {
+ l.setBegin(0);
+ l.setRealSize(0);
+ l.invalidate();
}
}
@JRubyClass(name = "Zlib::Deflate", parent = "Zlib::ZStream")
- public static class Deflate extends ZStream {
+ public static class JZlibDeflate extends ZStream {
public static final int BASE_SIZE = 100;
- private Deflater flater;
private int level;
private int windowBits;
private int strategy;
- private boolean dumpHeaderNeeded = false;
- private boolean dumpTrailerNeeded = false;
- private CRC32 checksum;
private ByteList collected;
protected static final ObjectAllocator DEFLATE_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new Deflate(runtime, klass);
+ return new JZlibDeflate(runtime, klass);
}
};
+ private com.jcraft.jzlib.Deflater flater = null;
+ private int flush = JZlib.Z_NO_FLUSH;
+
@JRubyMethod(name = "deflate", required = 1, optional = 1, meta = true, backtrace = true)
public static IRubyObject s_deflate(IRubyObject recv, IRubyObject[] args) {
Ruby runtime = recv.getRuntime();
args = Arity.scanArgs(runtime, args, 1, 1);
- int level = Deflater.DEFAULT_COMPRESSION;
+ int level = JZlib.Z_DEFAULT_COMPRESSION;
if (!args[1].isNil()) {
level = RubyNumeric.fix2int(args[1]);
checkLevel(runtime, level);
}
RubyClass klass = (RubyClass) recv;
- Deflate deflate = (Deflate) klass.allocate();
- deflate.init(level, MAX_WBITS, 8, Deflater.DEFAULT_STRATEGY);
+ JZlibDeflate deflate = (JZlibDeflate) klass.allocate();
+ deflate.init(level, JZlib.DEF_WBITS, 8, JZlib.Z_DEFAULT_STRATEGY);
try {
- IRubyObject result = deflate.deflate(args[0].convertToString().getByteList(), Z_FINISH);
+ IRubyObject result = deflate.deflate(args[0].convertToString().getByteList(), JZlib.Z_FINISH);
deflate.close();
return result;
} catch (IOException ioe) {
throw runtime.newIOErrorFromException(ioe);
}
}
- public Deflate(Ruby runtime, RubyClass type) {
+ public JZlibDeflate(Ruby runtime, RubyClass type) {
super(runtime, type);
}
@JRubyMethod(name = "initialize", optional = 4, visibility = PRIVATE, backtrace = true)
public IRubyObject _initialize(IRubyObject[] args) {
args = Arity.scanArgs(getRuntime(), args, 0, 4);
level = -1;
- windowBits = MAX_WBITS;
+ windowBits = JZlib.MAX_WBITS;
int memlevel = 8;
strategy = 0;
if (!args[0].isNil()) {
@@ -820,28 +833,49 @@ public IRubyObject _initialize(IRubyObject[] args) {
}
private void init(int level, int windowBits, int memlevel, int strategy) {
- // Zlib behavior: negative win_bits means no header and no checksum.
- boolean nowrap = false;
- if (windowBits < 0) {
- nowrap = true;
- } else if ((windowBits & 0x10) != 0) {
- nowrap = true; // gzip wrapper
- dumpHeaderNeeded = true;
- checksum = new CRC32();
- }
- flater = new Deflater(level, nowrap);
- flater.setStrategy(strategy);
+ flush = JZlib.Z_NO_FLUSH;
+ flater = new com.jcraft.jzlib.Deflater();
+
+ // TODO: Can we expect JZlib to check level, windowBits, and strategy here?
+ // Then we should remove checkLevel, checkWindowsBits and checkStrategy.
+ int err = flater.init(level, windowBits, memlevel);
+ if(err == com.jcraft.jzlib.JZlib.Z_STREAM_ERROR){
+ throw newStreamError(getRuntime(), "stream error");
+ }
+ err = flater.params(level, strategy);
+ if(err == com.jcraft.jzlib.JZlib.Z_STREAM_ERROR){
+ throw newStreamError(getRuntime(), "stream error");
+ }
+
collected = new ByteList(BASE_SIZE);
}
@Override
@JRubyMethod(visibility = PRIVATE)
- public IRubyObject initialize_copy(IRubyObject other) {
- if (this == other) {
+ public IRubyObject initialize_copy(IRubyObject _other) {
+ if (!(_other instanceof JZlibDeflate)) {
+ throw getRuntime().newTypeError("Expecting an instance of class JZlibDeflate");
+ }
+
+ if (this == _other) {
return this;
}
- // TODO: we cannot implement Deflate#dup as long as we use java.util.zip.Deflater...
- throw getRuntime().newNotImplementedError("Zlib::Deflate#dup is not supported");
+
+ JZlibDeflate other = (JZlibDeflate)_other;
+
+ this.level = other.level;
+ this.windowBits = other.windowBits;
+ this.strategy = other.strategy;
+ this.collected = (ByteList)other.collected.clone();
+
+ this.flush = other.flush;
+ this.flater = new com.jcraft.jzlib.Deflater();
+ int ret = this.flater.copy(other.flater);
+ if(ret != com.jcraft.jzlib.JZlib.Z_OK){
+ throw newStreamError(getRuntime(), "stream error");
+ }
+
+ return (IRubyObject)this;
}
@JRubyMethod(name = "<<", required = 1)
@@ -861,20 +895,32 @@ public IRubyObject params(ThreadContext context, IRubyObject level, IRubyObject
checkLevel(getRuntime(), l);
int s = RubyNumeric.fix2int(strategy);
checkStrategy(getRuntime(), s);
- flater.setLevel(l);
- flater.setStrategy(s);
+ if(flater.next_out==null)
+ flater.next_out=new byte[0];
+ flater.avail_out = flater.next_out.length;
+ flater.next_out_index = 0;
+ int err = flater.params(l, s);
+ if(err == com.jcraft.jzlib.JZlib.Z_STREAM_ERROR){
+ throw newStreamError(getRuntime(), "stream error");
+ }
+ if(flater.next_out_index>0)
+ collected.append(flater.next_out, 0, flater.next_out_index);
run();
return getRuntime().getNil();
}
@JRubyMethod(name = "set_dictionary", required = 1, backtrace = true)
public IRubyObject set_dictionary(ThreadContext context, IRubyObject arg) {
try {
- flater.setDictionary(arg.convertToString().getBytes());
+ byte [] tmp = arg.convertToString().getBytes();
+ int err = flater.setDictionary(tmp, tmp.length);
+ if(err == com.jcraft.jzlib.JZlib.Z_STREAM_ERROR){
+ throw newStreamError(context.getRuntime(), "stream error: ");
+ }
run();
return arg;
} catch (IllegalArgumentException iae) {
- throw Util.newStreamError(context.getRuntime(), "stream error: " + iae.getMessage());
+ throw newStreamError(context.getRuntime(), "stream error: " + iae.getMessage());
}
}
@@ -893,13 +939,13 @@ public IRubyObject flush(IRubyObject[] args) {
public IRubyObject deflate(IRubyObject[] args) {
args = Arity.scanArgs(getRuntime(), args, 1, 1);
if (internalFinished()) {
- throw Util.newStreamError(getRuntime(), "stream error");
+ throw newStreamError(getRuntime(), "stream error");
}
ByteList data = null;
if (!args[0].isNil()) {
data = args[0].convertToString().getByteList();
}
- int flush = Z_NO_FLUSH;
+ int flush = JZlib.Z_NO_FLUSH;
if (!args[1].isNil()) {
flush = RubyNumeric.fix2int(args[1]);
}
@@ -912,12 +958,12 @@ public IRubyObject deflate(IRubyObject[] args) {
@Override
protected int internalTotalIn() {
- return flater.getTotalIn();
+ return (int)flater.total_in;
}
@Override
protected int internalTotalOut() {
- return flater.getTotalOut();
+ return (int)flater.total_out;
}
@Override
@@ -937,7 +983,7 @@ public boolean internalFinished() {
@Override
protected long internalAdler() {
- return (checksum != null) ? checksum.getValue() : flater.getAdler() & 0xffffffffL;
+ return flater.getAdler();
}
@Override
@@ -951,29 +997,18 @@ protected void internalClose() {
}
private void append(ByteList obj) throws IOException {
- if (checksum != null) {
- if (dumpHeaderNeeded) {
- writeHeader();
- }
- checksum.update(obj.getUnsafeBytes(), obj.getBegin(), obj.getRealSize());
- }
- flater.setInput(obj.getUnsafeBytes(), obj.getBegin(), obj.getRealSize());
+ flater.setInput(obj.getUnsafeBytes(),
+ obj.getBegin(),
+ obj.getRealSize(), true);
run();
}
private IRubyObject flush(int flush) {
- if (flush == Z_NO_FLUSH) {
+ this.flush=flush;
+ if (flush == JZlib.Z_NO_FLUSH) {
return RubyString.newEmptyString(getRuntime());
}
- if (flush == Z_FINISH) {
- flater.finish();
- run();
- if (dumpTrailerNeeded) {
- writeTrailer();
- }
- } else {
- run();
- }
+ run();
IRubyObject obj = RubyString.newString(getRuntime(), collected);
collected = new ByteList(BASE_SIZE);
return obj;
@@ -987,37 +1022,30 @@ private IRubyObject deflate(ByteList str, int flush) throws IOException {
}
private IRubyObject finish() {
- return flush(Z_FINISH);
+ return flush(JZlib.Z_FINISH);
}
private void run() {
- if (flater.finished()) {
+ if(internalFinished())
return;
- }
byte[] outp = new byte[1024];
- while (!flater.finished()) {
- int resultLength = flater.deflate(outp);
- if (resultLength == 0) {
- break;
+ while (!internalFinished()){
+ flater.setOutput(outp);
+ int err = flater.deflate(flush);
+ switch(err){
+ case com.jcraft.jzlib.JZlib.Z_STREAM_ERROR:
+ throw newStreamError(getRuntime(), "stream error: ");
+ default:
}
- collected.append(outp, 0, resultLength);
- if (resultLength == outp.length) {
- outp = new byte[outp.length * 2];
+ int resultLength = flater.next_out_index;
+ if(resultLength == 0)
+ break;
+ collected.append(flater.next_out, 0, resultLength);
+ if (resultLength == flater.next_out.length && !internalFinished()) {
+ outp = new byte[flater.next_out.length * 2];
}
}
}
-
- private void writeHeader() throws IOException {
- collected.append(Util.dumpHeader(null, null, Z_DEFAULT_COMPRESSION, OS_CODE,
- System.currentTimeMillis()));
- dumpHeaderNeeded = false;
- dumpTrailerNeeded = true;
- }
-
- private void writeTrailer() {
- collected.append(Util.dumpTrailer(flater.getTotalIn(), (int) checksum.getValue()));
- dumpTrailerNeeded = false;
- }
}
@JRubyClass(name="Zlib::GzipFile")
@@ -1061,9 +1089,9 @@ public static IRubyObject wrap(ThreadContext context, IRubyObject recv, IRubyObj
// TODO: People extending GzipWriter/reader will break. Find better way here.
if (recv == runtime.getModule("Zlib").getClass("GzipWriter")) {
- instance = RubyGzipWriter.newInstance(recv, new IRubyObject[] { io }, block);
+ instance = JZlibRubyGzipWriter.newInstance(recv, new IRubyObject[] { io }, block);
} else {
- instance = RubyGzipReader.newInstance(recv, new IRubyObject[] { io }, block);
+ instance = JZlibRubyGzipReader.newInstance(recv, new IRubyObject[] { io }, block);
}
return wrapBlock(context, instance, block);
@@ -1096,6 +1124,7 @@ public static RubyGzipFile newInstance(IRubyObject recv, Block block) {
protected RubyTime mtime;
protected Encoding externalEncoding;
protected Encoding internalEncoding;
+ protected boolean sync = false;
public RubyGzipFile(Ruby runtime, RubyClass type) {
super(runtime, type);
@@ -1135,7 +1164,7 @@ protected boolean isClosed() {
@JRubyMethod(name = "orig_name")
public IRubyObject orig_name() {
if(closed) {
- throw Util.newGzipFileError(getRuntime(), "closed gzip stream");
+ throw newGzipFileError(getRuntime(), "closed gzip stream");
}
return nullFreeOrigName == null ? getRuntime().getNil() : nullFreeOrigName;
}
@@ -1148,7 +1177,7 @@ public IRubyObject to_io() {
@JRubyMethod(name = "comment")
public IRubyObject comment() {
if(closed) {
- throw Util.newGzipFileError(getRuntime(), "closed gzip stream");
+ throw newGzipFileError(getRuntime(), "closed gzip stream");
}
return nullFreeComment == null ? getRuntime().getNil() : nullFreeComment;
}
@@ -1165,7 +1194,7 @@ public IRubyObject mtime() {
@JRubyMethod(name = "sync")
public IRubyObject sync() {
- return getRuntime().getNil();
+ return sync ? getRuntime().getTrue() : getRuntime().getFalse();
}
@JRubyMethod(name = "finish")
@@ -1188,27 +1217,28 @@ public IRubyObject level() {
}
@JRubyMethod(name = "sync=", required = 1)
- public IRubyObject set_sync(IRubyObject ignored) {
- return getRuntime().getNil();
+ public IRubyObject set_sync(IRubyObject arg) {
+ sync = ((RubyBoolean)arg).isTrue();
+ return sync ? getRuntime().getTrue() : getRuntime().getFalse();
}
}
@JRubyClass(name="Zlib::GzipReader", parent="Zlib::GzipFile", include="Enumerable")
- public static class RubyGzipReader extends RubyGzipFile {
+ public static class JZlibRubyGzipReader extends RubyGzipFile {
@JRubyClass(name="Zlib::GzipReader::Error", parent="Zlib::GzipReader")
public static class Error {}
protected static final ObjectAllocator GZIPREADER_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyGzipReader(runtime, klass);
+ return new JZlibRubyGzipReader(runtime, klass);
}
};
-
+
@JRubyMethod(name = "new", rest = true, meta = true)
- public static RubyGzipReader newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
+ public static JZlibRubyGzipReader newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
RubyClass klass = (RubyClass)recv;
- RubyGzipReader result = (RubyGzipReader)klass.allocate();
+ JZlibRubyGzipReader result = (JZlibRubyGzipReader)klass.allocate();
result.callInit(args, block);
return result;
}
@@ -1217,224 +1247,54 @@ public static RubyGzipReader newInstance(IRubyObject recv, IRubyObject[] args, B
public static IRubyObject open18(final ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
Ruby runtime = recv.getRuntime();
IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", args[0], runtime.newString("rb"));
- RubyGzipReader gzio = newInstance(recv, new IRubyObject[] { io }, block);
+ JZlibRubyGzipReader gzio = newInstance(recv, new IRubyObject[] { io }, block);
return RubyGzipFile.wrapBlock(context, gzio, block);
}
@JRubyMethod(name = "open", required = 1, optional = 1, meta = true, compat = RUBY1_9)
public static IRubyObject open19(final ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
Ruby runtime = recv.getRuntime();
IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", args[0], runtime.newString("rb"));
- RubyGzipReader gzio = newInstance(recv, argsWithIo(io, args), block);
+ JZlibRubyGzipReader gzio = newInstance(recv, argsWithIo(io, args), block);
return RubyGzipFile.wrapBlock(context, gzio, block);
}
-
- public RubyGzipReader(Ruby runtime, RubyClass type) {
+
+ public JZlibRubyGzipReader(Ruby runtime, RubyClass type) {
super(runtime, type);
}
private int line;
private long position;
- private HeaderReadableGZIPInputStream io;
+ private com.jcraft.jzlib.GZIPInputStream io;
private InputStream bufferedStream;
- /**
- * IOInputStream wrapper for counting and keeping reading position.
- */
- private static class CountingIOInputStream extends IOInputStream {
-
- private int position;
- IRubyObject io;
-
- public CountingIOInputStream(IRubyObject io) {
- super(io);
- this.io = io;
- position = 0;
- }
-
- @Override
- public int read() throws IOException {
- int ret = super.read();
- if (ret != -1) {
- position++;
- }
- return ret;
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- int ret = super.read(b);
- if (ret != -1) {
- position += ret;
- }
- return ret;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int ret = super.read(b, off, len);
- if (ret != -1) {
- position += ret;
- }
- return ret;
- }
-
- int pos() {
- return position;
- }
-
- Ruby getRuntime() {
- return io.getRuntime();
- }
- }
-
- private class HeaderReadableGZIPInputStream extends InflaterInputStream {
-
- private final static int DEFAULT_BUFFER_SIZE = 512;
- // saame as InputStream#in
- private CountingIOInputStream countingStream;
- private CRC32 checksum = new CRC32();
- private boolean eof = false;
-
- /**
- * Offers header property in addition to GZIPInputStream.
- */
- public HeaderReadableGZIPInputStream(CountingIOInputStream io) {
- super(new BufferedInputStream(io), new Inflater(true), DEFAULT_BUFFER_SIZE);
- this.countingStream = io;
- parseHeader(io);
- eof = false;
- checksum.reset();
- }
-
- @Override
- public int read() throws IOException {
- if (eof) {
- return -1;
- }
- int ret = super.read();
- if (ret == -1) {
- parseTrailer();
- } else {
- checksum.update((byte) (ret & 0xff));
- }
- return ret;
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- if (eof) {
- return -1;
- }
- int ret = super.read(b);
- if (ret == -1) {
- parseTrailer();
- } else {
- checksum.update(b, 0, ret);
- }
- return ret;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (eof) {
- return -1;
- }
- int ret = super.read(b, off, len);
- if (ret == -1) {
- parseTrailer();
- } else {
- checksum.update(b, off, ret);
- }
- return ret;
- }
-
- /**
- * We call internal IO#close directly, not via IOInputStream#close.
- * IOInputStream#close directly invoke IO.getOutputStream().close()
- * for IO object instead of just calling IO#cloase of Ruby.
- * It causes EBADF at OpenFile#finalize.
- *
- * CAUTION: CountingIOInputStream#close does not called even if it exists.
- *
- * TODO: implement this without IOInputStream? Not so hard.
- */
- @Override
- public void close() throws IOException {
- // Do not invoke DeflaterOutputStream#close here.
- // following works as same as DeflaterOutputStream#close.
- //super.close();
- if (!closed) {
- // we don't want to invoke IOInputStream#close for now.
- // in.close();
- closed = true;
- }
- // call IO#close instead.
- if (countingStream.io.respondsTo("close")) {
- countingStream.io.callMethod(countingStream.getRuntime().getCurrentContext(), "close");
- }
- eof = true;
- }
-
- public int pos() {
- return countingStream.pos();
- }
-
- public long crc() {
- return checksum.getValue();
- }
-
- private void parseHeader(CountingIOInputStream io) {
- Util.GzipHeader header = Util.readHeader(io.getRuntime(), in);
- if (header == null) {
- throw Util.newGzipFileError(io.getRuntime(), "not in gzip format");
- }
- mtime.setDateTime(header.mtime);
- level = header.level;
- osCode = header.osCode;
- if (header.origName != null) {
- nullFreeOrigName = io.getRuntime().newString(header.origName);
- nullFreeOrigName.setTaint(true);
- }
- if (header.comment != null) {
- nullFreeComment = io.getRuntime().newString(header.comment);
- nullFreeComment.setTaint(true);
- }
- }
-
- private void parseTrailer() {
- try {
- eof = true;
- int rest = 8;
- byte[] trailer = new byte[8];
- int remaining = super.inf.getRemaining();
- if (remaining > 0) {
- System.arraycopy(super.buf, super.len - remaining, trailer, 0,
- (remaining > 8) ? 8 : remaining);
- rest -= remaining;
- }
- while (rest > 0) {
- int ret = in.read(trailer, 8 - rest, rest);
- if (ret == -1) {
- throw new EOFException();
- }
- rest -= ret;
- }
- Util.checkTrailer(countingStream.getRuntime(), trailer,
- (super.inf.getBytesWritten() & 0xffffffffL), checksum.getValue());
- } catch (IOException ignored) {
- throw Util.newNoFooter(countingStream.getRuntime(),
- "footer is not found");
- }
- }
- }
-
@JRubyMethod(name = "initialize", visibility = PRIVATE, compat = RUBY1_8)
public IRubyObject initialize(IRubyObject stream) {
realIo = stream;
line = 0;
position = 0;
- io = new HeaderReadableGZIPInputStream(new CountingIOInputStream(realIo));
+ try {
+ io = new com.jcraft.jzlib.GZIPInputStream(new IOInputStream(realIo),
+ 512,
+ false); // don't close realIO
+ // JRUBY-4502
+ // CRuby expects to parse gzip header in 'new'.
+ io.readHeader();
+ }
+ catch(IOException e){
+ RaiseException re = newGzipFileError(getRuntime(),
+ "not in gzip format");
+ if (getRuntime().is1_9()) {
+ byte[] input = io.getAvailIn();
+ if(input!=null && input.length>0){
+ ByteList i = new ByteList(input, 0, input.length);
+ RubyException rubye = re.getException();
+ rubye.setInstanceVariable("@input",
+ RubyString.newString(getRuntime(), i));
+ }
+ }
+ throw re;
+ }
bufferedStream = new BufferedInputStream(io);
return this;
}
@@ -1454,7 +1314,7 @@ public IRubyObject initialize19(IRubyObject[] args) {
obj.getSingletonClass().defineMethod("path", new Callback() {
public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
- return ((RubyGzipReader) recv).realIo.callMethod(recv.getRuntime()
+ return ((JZlibRubyGzipReader) recv).realIo.callMethod(recv.getRuntime()
.getCurrentContext(), "path");
}
@@ -1471,7 +1331,7 @@ public IRubyObject rewind() {
Ruby rt = getRuntime();
// should invoke seek on realIo...
realIo.callMethod(rt.getCurrentContext(), "seek",
- new IRubyObject[]{rt.newFixnum(-io.pos()), rt.newFixnum(Stream.SEEK_CUR)});
+ new IRubyObject[]{rt.newFixnum(-io.getTotalIn()), rt.newFixnum(Stream.SEEK_CUR)});
// ... and then reinitialize
initialize(realIo);
return getRuntime().getNil();
@@ -1523,7 +1383,7 @@ private IRubyObject internalGets(IRubyObject[] args) throws IOException {
private IRubyObject internalSepGets(ByteList sep) throws IOException {
return internalSepGets(sep, -1);
}
-
+
private IRubyObject internalSepGets(ByteList sep, int limit) throws IOException {
ByteList result = new ByteList();
if (sep.getRealSize() == 0) sep = Stream.PARAGRAPH_SEPARATOR;
@@ -1572,7 +1432,7 @@ public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
}
private final static int BUFF_SIZE = 4096;
-
+
@JRubyMethod(name = "read", optional = 1)
public IRubyObject read(IRubyObject[] args) {
try {
@@ -1586,9 +1446,22 @@ public IRubyObject read(IRubyObject[] args) {
return readSize(len);
}
return RubyString.newEmptyString(getRuntime());
- } catch (IOException ioe) {
- throw getRuntime().newIOErrorFromException(ioe);
- }
+ }
+ catch (IOException ioe) {
+ String m = ioe.getMessage();
+ if (m.startsWith("Unexpected end of ZLIB input stream"))
+ throw newGzipFileError(getRuntime(), ioe.getMessage());
+ else if (m.startsWith("footer is not found"))
+ throw newNoFooter(getRuntime(), "footer is not found");
+ else if (m.startsWith("incorrect data check"))
+ throw newCRCError(getRuntime(),
+ "invalid compressed data -- crc error");
+ else if (m.startsWith("incorrect length check"))
+ throw newLengthError(getRuntime(),
+ "invalid compressed data -- length error");
+ else
+ throw newDataError(getRuntime(), ioe.getMessage());
+ }
}
@JRubyMethod(name = "readpartial", required = 1, optional = 1)
@@ -1709,7 +1582,7 @@ public IRubyObject getc() {
public IRubyObject getbyte() {
return getc();
}
-
+
@JRubyMethod(name = "getc", compat = RUBY1_9)
public IRubyObject getc_19() {
try {
@@ -1748,15 +1621,31 @@ private boolean isEof() throws IOException {
public IRubyObject close() {
if (!closed) {
try {
+ /**
+ * We call internal IO#close directly, not via IOInputStream#close.
+ * IOInputStream#close directly invoke IO.getOutputStream().close()
+ * for IO object instead of just calling IO#cloase of Ruby.
+ * It causes EBADF at OpenFile#finalize.
+ *
+ * CAUTION: bufferedStream.close() will not cause
+ * 'IO.getOutputStream().close()', becase 'false' has been
+ * given as third augument in constructing GZIPInputStream.
+ *
+ * TODO: implement this without IOInputStream? Not so hard.
+ */
bufferedStream.close();
- } catch (IOException ioe) {
+ if (realIo.respondsTo("close")) {
+ realIo.callMethod(realIo.getRuntime().getCurrentContext(), "close");
+ }
+ }
+ catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
}
}
this.closed = true;
return getRuntime().getNil();
}
-
+
@JRubyMethod(name = "eof")
public IRubyObject eof() {
try {
@@ -1773,16 +1662,52 @@ public IRubyObject eof_p() {
@JRubyMethod
public IRubyObject unused() {
- // TODO: implement
- return getRuntime().getNil();
+ byte[] tmp = io.getAvailIn();
+ if(tmp == null)
+ return getRuntime().getNil();
+ else
+ return RubyString.newString(getRuntime(), tmp);
}
@Override
@JRubyMethod
public IRubyObject crc() {
- return getRuntime().newFixnum(io.crc());
+ long crc = 0;
+ try {
+ crc=io.getCRC();
+ }
+ catch(com.jcraft.jzlib.GZIPException e){
+ }
+ return getRuntime().newFixnum(crc);
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject os_code() {
+ int os = io.getOS();
+ if(os == 255)
+ os = (byte) 0x0b; // NTFS filesystem (NT),
+ // because CRuby's test_zlib expect it.
+ return getRuntime().newFixnum(os & 0xff);
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject orig_name() {
+ String name = io.getName();
+ nullFreeOrigName = getRuntime().newString(name);
+ return super.orig_name();
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject comment() {
+ String comment = io.getComment();
+ nullFreeComment = getRuntime().newString(comment);
+ return super.comment();
}
+
@JRubyMethod(optional = 1)
public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block) {
ByteList sep = ((RubyString) getRuntime().getGlobalVariables().get("$/")).getByteList();
@@ -1801,9 +1726,9 @@ public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block)
@JRubyMethod(optional = 1)
public IRubyObject each_line(ThreadContext context, IRubyObject[] args, Block block) {
- return each(context, args, block);
+ return each(context, args, block);
}
-
+
@JRubyMethod
public IRubyObject ungetc(IRubyObject arg) {
return getRuntime().getNil();
@@ -1847,17 +1772,17 @@ public IRubyObject each_byte(ThreadContext context, Block block) {
}
@JRubyClass(name="Zlib::GzipWriter", parent="Zlib::GzipFile")
- public static class RubyGzipWriter extends RubyGzipFile {
+ public static class JZlibRubyGzipWriter extends RubyGzipFile {
protected static final ObjectAllocator GZIPWRITER_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyGzipWriter(runtime, klass);
+ return new JZlibRubyGzipWriter(runtime, klass);
}
};
-
+
@JRubyMethod(name = "new", rest = true, meta = true)
- public static RubyGzipWriter newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
+ public static JZlibRubyGzipWriter newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
RubyClass klass = (RubyClass)recv;
- RubyGzipWriter result = (RubyGzipWriter)klass.allocate();
+ JZlibRubyGzipWriter result = (JZlibRubyGzipWriter)klass.allocate();
result.callInit(args, block);
return result;
}
@@ -1866,105 +1791,24 @@ public static RubyGzipWriter newInstance(IRubyObject recv, IRubyObject[] args, B
public static IRubyObject open18(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
Ruby runtime = recv.getRuntime();
IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", args[0], runtime.newString("wb"));
- RubyGzipWriter gzio = newInstance(recv, argsWithIo(io, args), block);
+ JZlibRubyGzipWriter gzio = newInstance(recv, argsWithIo(io, args), block);
return RubyGzipFile.wrapBlock(context, gzio, block);
}
@JRubyMethod(name = "open", required = 1, optional = 3, meta = true, compat = RUBY1_9)
public static IRubyObject open19(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
Ruby runtime = recv.getRuntime();
IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", args[0], runtime.newString("wb"));
- RubyGzipWriter gzio = newInstance(recv, argsWithIo(io, args), block);
+ JZlibRubyGzipWriter gzio = newInstance(recv, argsWithIo(io, args), block);
return RubyGzipFile.wrapBlock(context, gzio, block);
}
- public RubyGzipWriter(Ruby runtime, RubyClass type) {
+ public JZlibRubyGzipWriter(Ruby runtime, RubyClass type) {
super(runtime, type);
}
- public class HeaderModifyableGZIPOutputStream extends DeflaterOutputStream {
-
- private IRubyObject io;
- private long position;
- private CRC32 checksum = new CRC32();
- private boolean headerIsWritten = false;
- private long modifiedTime = System.currentTimeMillis();
- private final static int DEFAULT_BUFFER_SIZE = 512;
-
- public HeaderModifyableGZIPOutputStream(IRubyObject io) throws IOException {
- super(new IOOutputStream(io, false, false), new Deflater(Deflater.DEFAULT_COMPRESSION, true), DEFAULT_BUFFER_SIZE);
- this.io = io;
- position = 0;
- }
-
- /**
- * We call internal IO#close directly, not via IOOutputStream#close.
- * IOInputStream#close directly invoke IO.getOutputStream().close()
- * for IO object instead of just calling IO#cloase of Ruby.
- * It causes EBADF at OpenFile#finalize.
- * TODO: implement this without IOOutputStream? Not so hard.
- */
- @Override
- public void close() throws IOException {
- // Do not invoke DeflaterOutputStream#close here.
- // following works as same as DeflaterOutputStream#close.
- //super.close();
- if (!closed) {
- finish();
- // out.close(); // we don't want to invoke IOInputStream#close for now.
- closed = true;
- }
- // call IO#close instead.
- if (io.respondsTo("close")) {
- io.callMethod(io.getRuntime().getCurrentContext(), "close");
- }
- }
-
- @Override
- public synchronized void write(byte bytes[], int offset, int length) throws IOException {
- writeHeaderIfNeeded();
- super.write(bytes, offset, length);
- checksum.update(bytes, offset, length);
- position += length;
- }
-
- @Override
- public void finish() throws IOException {
- writeHeaderIfNeeded();
- super.finish();
- out.write(Util.dumpTrailer(def.getTotalIn(), (int) checksum.getValue()));
- }
-
- public void setModifiedTime(long newModifiedTime) {
- modifiedTime = newModifiedTime;
- }
-
- public boolean headerIsWritten() {
- return headerIsWritten;
- }
-
- public long crc() {
- return checksum.getValue();
- }
-
- public long pos() {
- return position;
- }
-
- // Called before any write to make sure the
- // header is always written before the first bytes
- private void writeHeaderIfNeeded() throws IOException {
- if (headerIsWritten == false) {
- out.write(Util.dumpHeader((nullFreeOrigName != null ? nullFreeOrigName.toString()
- : null), (nullFreeComment != null ? nullFreeComment.toString() : null),
- level, OS_CODE, modifiedTime));
- headerIsWritten = true;
- }
- }
- }
+ private com.jcraft.jzlib.GZIPOutputStream io;
- private HeaderModifyableGZIPOutputStream io;
-
@JRubyMethod(name = "initialize", required = 1, rest = true, visibility = PRIVATE, compat = RUBY1_8)
public IRubyObject initialize(IRubyObject[] args) {
// args: recv, path, opts = {}
@@ -1977,7 +1821,7 @@ public IRubyObject initialize(IRubyObject[] args) {
private IRubyObject initializeCommon(IRubyObject stream) {
realIo = (RubyObject) stream;
try {
- io = new HeaderModifyableGZIPOutputStream(realIo);
+ io = new com.jcraft.jzlib.GZIPOutputStream(new IOOutputStream(realIo, false, false), 512, false);
return this;
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
@@ -2006,7 +1850,7 @@ public IRubyObject initialize19(IRubyObject[] args, Block unused) {
obj.getSingletonClass().defineMethod("path", new Callback() {
public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
- return ((RubyGzipWriter) recv).realIo.callMethod(recv.getRuntime()
+ return ((JZlibRubyGzipWriter) recv).realIo.callMethod(recv.getRuntime()
.getCurrentContext(), "path");
}
@@ -2020,7 +1864,7 @@ public Arity getArity() {
private static void checkLevel(Ruby runtime, int level) {
if (level < 0 || level > 9) {
- throw Util.newStreamError(runtime, "stream error: invalid level");
+ throw newStreamError(runtime, "stream error: invalid level");
}
}
@@ -2030,6 +1874,9 @@ public IRubyObject close() {
if (!closed) {
try {
io.close();
+ if (realIo.respondsTo("close")) {
+ realIo.callMethod(realIo.getRuntime().getCurrentContext(), "close");
+ }
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
}
@@ -2057,37 +1904,43 @@ public IRubyObject print(IRubyObject[] args) {
write(args[i]);
}
}
-
+
IRubyObject sep = getRuntime().getGlobalVariables().get("$\\");
if (!sep.isNil()) {
write(sep);
}
-
+
return getRuntime().getNil();
}
@JRubyMethod(name = {"pos", "tell"})
public IRubyObject pos() {
- return RubyNumeric.int2fix(getRuntime(), io.pos());
+ return RubyNumeric.int2fix(getRuntime(), io.getTotalIn());
}
@JRubyMethod(name = "orig_name=", required = 1)
public IRubyObject set_orig_name(IRubyObject obj) {
- if (io.headerIsWritten()) {
- throw Util.newGzipFileError(getRuntime(), "header is already written");
- }
nullFreeOrigName = obj.convertToString();
ensureNonNull(nullFreeOrigName);
+ try{
+ io.setName(nullFreeOrigName.toString());
+ }
+ catch(com.jcraft.jzlib.GZIPException e){
+ throw newGzipFileError(getRuntime(), "header is already written");
+ }
return obj;
}
@JRubyMethod(name = "comment=", required = 1)
public IRubyObject set_comment(IRubyObject obj) {
- if (io.headerIsWritten()) {
- throw Util.newGzipFileError(getRuntime(), "header is already written");
- }
nullFreeComment = obj.convertToString();
ensureNonNull(nullFreeComment);
+ try{
+ io.setComment(nullFreeComment.toString());
+ }
+ catch(com.jcraft.jzlib.GZIPException e){
+ throw newGzipFileError(getRuntime(), "header is already written");
+ }
return obj;
}
@@ -2108,13 +1961,13 @@ public IRubyObject putc(IRubyObject p1) {
throw getRuntime().newIOErrorFromException(ioe);
}
}
-
+
@JRubyMethod(name = "puts", rest = true)
public IRubyObject puts(ThreadContext context, IRubyObject[] args) {
- RubyStringIO sio = (RubyStringIO)getRuntime().fastGetClass("StringIO").newInstance(context, new IRubyObject[0], Block.NULL_BLOCK);
+ RubyStringIO sio = (RubyStringIO)getRuntime().getClass("StringIO").newInstance(context, new IRubyObject[0], Block.NULL_BLOCK);
sio.puts(context, args);
write(sio.string());
-
+
return getRuntime().getNil();
}
@@ -2133,36 +1986,54 @@ public IRubyObject finish() {
@JRubyMethod(name = "flush", optional = 1)
public IRubyObject flush(IRubyObject[] args) {
- if (args.length == 0 || args[0].isNil() || RubyNumeric.fix2int(args[0]) != 0) { // Zlib::NO_FLUSH
- try {
- io.flush();
- } catch (IOException ioe) {
- throw getRuntime().newIOErrorFromException(ioe);
+ int flush = com.jcraft.jzlib.JZlib.Z_SYNC_FLUSH;
+ if (args.length > 0 && !args[0].isNil()) {
+ flush = RubyNumeric.fix2int(args[0]);
+ }
+ boolean tmp = io.getSyncFlush();
+ try {
+ if(flush!=0 /*NO_FLUSH*/){
+ io.setSyncFlush(true);
}
+ io.flush();
+ } catch (IOException ioe) {
+ throw getRuntime().newIOErrorFromException(ioe);
+ }
+ finally {
+ io.setSyncFlush(tmp);
}
return getRuntime().getNil();
}
@JRubyMethod(name = "mtime=", required = 1)
public IRubyObject set_mtime(IRubyObject arg) {
- if (io.headerIsWritten()) {
- throw Util.newGzipFileError(getRuntime(), "header is already written");
- }
if (arg instanceof RubyTime) {
this.mtime = ((RubyTime) arg);
} else if (arg.isNil()) {
// ...nothing
} else {
this.mtime.setDateTime(new DateTime(RubyNumeric.fix2long(arg) * 1000));
}
- io.setModifiedTime(this.mtime.to_i().getLongValue());
+ try{
+ io.setModifiedTime(this.mtime.to_i().getLongValue());
+ }
+ catch(com.jcraft.jzlib.GZIPException e){
+ throw newGzipFileError(getRuntime(), "header is already written");
+ }
return getRuntime().getNil();
}
@Override
@JRubyMethod(name = "crc")
public IRubyObject crc() {
- return getRuntime().newFixnum(io.crc());
+ long crc = 0L;
+ try {
+ crc = io.getCRC();
+ }
+ catch(com.jcraft.jzlib.GZIPException e){
+ // not calculated yet
+ }
+ return getRuntime().newFixnum(crc);
}
@JRubyMethod(name = "write", required = 1)
@@ -2177,11 +2048,74 @@ public IRubyObject write(IRubyObject p1) {
}
}
try {
- io.write(bytes.getUnsafeBytes(), bytes.begin(), bytes.length());
+ // TODO: jzlib-1.1.0.jar throws IndexOutOfBoundException for zero length buffer.
+ if (bytes.length() > 0) {
+ io.write(bytes.getUnsafeBytes(), bytes.begin(), bytes.length());
+ }
return getRuntime().newFixnum(bytes.length());
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
}
}
+
+ @Override
+ @JRubyMethod
+ public IRubyObject set_sync(IRubyObject arg) {
+ IRubyObject s = super.set_sync(arg);
+ io.setSyncFlush(sync);
+ return s;
+ }
+
+ }
+
+ static RaiseException newZlibError(Ruby runtime, String message) {
+ return newZlibError(runtime, "Error", message);
+ }
+
+ static RaiseException newBufError(Ruby runtime, String message) {
+ return newZlibError(runtime, "BufError", message);
+ }
+
+ static RaiseException newDictError(Ruby runtime, String message) {
+ return newZlibError(runtime, "NeedDict", message);
+ }
+
+ static RaiseException newStreamError(Ruby runtime, String message) {
+ return newZlibError(runtime, "StreamError", message);
+ }
+
+ static RaiseException newDataError(Ruby runtime, String message) {
+ return newZlibError(runtime, "DataError", message);
+ }
+
+ private static RaiseException newZlibError(Ruby runtime, String klass, String message) {
+ RubyClass errorClass = runtime.getModule("Zlib").getClass(klass);
+ return new RaiseException(RubyException.newException(runtime, errorClass, message), true);
+ }
+
+ static RaiseException newGzipFileError(Ruby runtime, String message) {
+ return newGzipFileError(runtime, "Error", message);
+ }
+
+ static RaiseException newCRCError(Ruby runtime, String message) {
+ return newGzipFileError(runtime, "CRCError", message);
+ }
+
+ static RaiseException newNoFooter(Ruby runtime, String message) {
+ return newGzipFileError(runtime, "NoFooter", message);
+ }
+
+ static RaiseException newLengthError(Ruby runtime, String message) {
+ return newGzipFileError(runtime, "LengthError", message);
+ }
+
+ private static RaiseException newGzipFileError(Ruby runtime, String klass, String message) {
+ RubyClass errorClass = runtime.getModule("Zlib").getClass("GzipFile").getClass(klass);
+ RubyException excn = RubyException.newException(runtime, errorClass, message);
+ if (runtime.is1_9()) {
+ // TODO: not yet supported. rewrite GzipReader/Writer with Inflate/Deflate?
+ excn.setInstanceVariable("@input", runtime.getNil());
+ }
+ return new RaiseException(excn, true);
}
}
View
248 src/org/jruby/ext/zlib/Util.java
@@ -1,248 +0,0 @@
-package org.jruby.ext.zlib;
-
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.joda.time.DateTime;
-import org.jruby.Ruby;
-import org.jruby.RubyClass;
-import org.jruby.RubyException;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.util.ByteList;
-
-import static org.jruby.ext.zlib.Zlib.*;
-
-public final class Util {
- private Util() {
- }
-
- public static byte[] dumpHeader(String origName, String comment, int level, byte osCode,
- long modifiedTime) throws IOException {
- // See http://www.gzip.org/zlib/rfc-gzip.html
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte flags = 0, extraflags = 0;
- if (origName != null) {
- flags |= GZ_FLAG_ORIG_NAME;
- }
- if (comment != null) {
- flags |= GZ_FLAG_COMMENT;
- }
- if (level == Z_BEST_SPEED) {
- extraflags |= GZ_EXTRAFLAG_FAST;
- } else if (level == Z_BEST_COMPRESSION) {
- extraflags |= GZ_EXTRAFLAG_SLOW;
- }
- byte header[] = { GZ_MAGIC_ID_1, GZ_MAGIC_ID_2, GZ_METHOD_DEFLATE, flags,
- (byte) (modifiedTime), (byte) (modifiedTime >> 8), (byte) (modifiedTime >> 16),
- (byte) (modifiedTime >> 24), extraflags, OS_CODE };
- out.write(header);
- if (origName != null) {
- out.write(origName.toString().getBytes());
- out.write('\0');
- }
- if (comment != null) {
- out.write(comment.toString().getBytes());
- out.write('\0');
- }
- return out.toByteArray();
- }
-
- public static byte[] dumpTrailer(int originalDataSize, int checksumInt) {
- return new byte[] { (byte) (checksumInt), (byte) (checksumInt >> 8),
- (byte) (checksumInt >> 16), (byte) (checksumInt >> 24), (byte) (originalDataSize),
- (byte) (originalDataSize >> 8), (byte) (originalDataSize >> 16),
- (byte) (originalDataSize >> 24) };
- }
-
- public static class GzipHeader {
- public DateTime mtime;
- public int level;
- public byte osCode;
- public String origName;
- public String comment;
- public int length;
-
- public GzipHeader() {
- }
- }
-
- public static GzipHeader readHeader(Ruby runtime, InputStream in) {
- GzipHeader header = new GzipHeader();
- try {
- if ((byte) readUByte(in) != GZ_MAGIC_ID_1) {
- return null;
- }
- if ((byte) readUByte(in) != GZ_MAGIC_ID_2) {
- return null;
- }
- byte b = (byte) readUByte(in);
- if ((byte) b != GZ_METHOD_DEFLATE) {
- throw newGzipFileError(runtime, "unsupported compression method " + b);
- }
- int flags = readUByte(in);
- if ((flags & GZ_FLAG_MULTIPART) != 0) {
- throw newGzipFileError(runtime,
- "multi-part gzip file is not supported");
- } else if ((flags & GZ_FLAG_ENCRYPT) != 0) {
- throw newGzipFileError(runtime, "encrypted gzip file is not supported");
- } else if ((flags & GZ_FLAG_UNKNOWN_MASK) != 0) {
- throw newGzipFileError(runtime, "unknown flags " + flags);
- }
- header.mtime = new DateTime(readUInt(in) * 1000);
- int extraflags = readUByte(in);
- if ((extraflags & GZ_EXTRAFLAG_FAST) != 0) {
- header.level = Z_BEST_SPEED;
- } else if ((extraflags & GZ_EXTRAFLAG_SLOW) != 0) {
- header.level = Z_BEST_COMPRESSION;
- } else {
- header.level = Z_DEFAULT_COMPRESSION;
- }
- header.osCode = (byte) readUByte(in);
- header.length += 10;
- if ((flags & GZ_FLAG_EXTRA) != 0) {
- int size = readUShort(in);
- byte[] extra = new byte[2 + size];
- // just discards it
- readBytes(in, extra);
- header.length += 2 + extra.length;
- }
- if ((flags & GZ_FLAG_ORIG_NAME) != 0) {
- header.origName = readNullTerminateString(in);
- header.length += header.origName.getBytes().length + 1;
- }
- if ((flags & GZ_FLAG_COMMENT) != 0) {
- header.comment = readNullTerminateString(in);
- header.length += header.comment.getBytes().length + 1;
- }
- } catch (IOException ioe) {
- String msg = ioe.getMessage();
- if (msg == null || msg.length() == 0) {
- msg = "not in gzip format";
- }
- throw newGzipFileError(runtime, msg);
- }
- // TODO: should check header CRC (cruby-zlib doesn't do for now)
- return header;
- }
-
- public static void checkTrailer(Ruby runtime, byte[] trailer, long bytesWritten, long checksum) {
- long uint = bytesToUInt(trailer, 0);
- if (uint != checksum) {
- throw newCRCError(runtime, "invalid compressed data -- crc error");
- }
- uint = bytesToUInt(trailer, 4);
- if (uint != bytesWritten) {
- throw newLengthError(runtime, "invalid compressed data -- length error");
- }
- }
-
- public static void resetBuffer(ByteList l) {
- l.setBegin(0);
- l.setRealSize(0);
- l.invalidate();
- }
-
- public static RaiseException newZlibError(Ruby runtime, String message) {
- return newZlibError(runtime, "Error", message);
- }
-
- public static RaiseException newBufError(Ruby runtime, String message) {
- return newZlibError(runtime, "BufError", message);
- }
-
- public static RaiseException newDictError(Ruby runtime, String message) {
- return newZlibError(runtime, "NeedDict", message);
- }
-
- public static RaiseException newStreamError(Ruby runtime, String message) {
- return newZlibError(runtime, "StreamError", message);
- }
-
- public static RaiseException newDataError(Ruby runtime, String message) {
- return newZlibError(runtime, "DataError", message);
- }
-