From 923c4661e82b3e0a308a57dda003dd1ea7c034f7 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 17 Sep 2007 16:15:16 -0600 Subject: [PATCH] implement Inflater and InflaterInputStream --- classpath/java-util-zip.cpp | 75 ++++++++++++ .../java/util/zip/DataFormatException.java | 11 ++ classpath/java/util/zip/Inflater.java | 110 ++++++++++++++++++ .../java/util/zip/InflaterInputStream.java | 49 +++++++- 4 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 classpath/java-util-zip.cpp create mode 100644 classpath/java/util/zip/DataFormatException.java create mode 100644 classpath/java/util/zip/Inflater.java diff --git a/classpath/java-util-zip.cpp b/classpath/java-util-zip.cpp new file mode 100644 index 0000000000..17ac1ca1a0 --- /dev/null +++ b/classpath/java-util-zip.cpp @@ -0,0 +1,75 @@ +#include "stdlib.h" +#include "string.h" +#include "zlib.h" + +#include "jni.h" +#include "jni-util.h" + +#undef JNIEXPORT +#define JNIEXPORT __attribute__ ((visibility("default"))) + +extern "C" JNIEXPORT jlong JNICALL +Java_java_util_zip_Inflater_make +(JNIEnv* e, jclass, jboolean nowrap) +{ + z_stream* s = static_cast(malloc(sizeof(z_stream))); + memset(s, 0, sizeof(z_stream)); + + int r = inflateInit2(s, (nowrap ? -15 : 15)); + if (r != Z_OK) { + free(s); + throwNew(e, "java/lang/RuntimeException", zError(r)); + return 0; + } + + return reinterpret_cast(s); +} + +extern "C" JNIEXPORT void JNICALL +Java_java_util_zip_Inflater_dispose(JNIEnv*, jclass, jlong peer) +{ + z_stream* s = reinterpret_cast(peer); + inflateEnd(s); + free(s); +} + +extern "C" JNIEXPORT void JNICALL +Java_java_util_zip_Inflater_inflate +(JNIEnv* e, jclass, jlong peer, + jbyteArray input, jint inputOffset, jint inputLength, + jbyteArray output, jint outputOffset, jint outputLength, + jintArray results) +{ + z_stream* s = reinterpret_cast(peer); + + jbyte* in = static_cast(malloc(inputLength)); + if (in == 0) { + throwNew(e, "java/lang/OutOfMemoryError", 0); + return; + } + + jbyte* out = static_cast(malloc(outputLength)); + if (out == 0) { + free(in); + throwNew(e, "java/lang/OutOfMemoryError", 0); + return; + } + + e->GetByteArrayRegion(input, inputOffset, inputLength, in); + + s->next_in = reinterpret_cast(in); + s->avail_in = inputLength; + s->next_out = reinterpret_cast(out); + s->avail_out = outputLength; + + int r = inflate(s, Z_SYNC_FLUSH); + jint resultArray[3] + = { r, inputLength - s->avail_in, outputLength - s->avail_out }; + + free(in); + + e->SetByteArrayRegion(output, outputOffset, resultArray[2], out); + free(out); + + e->SetIntArrayRegion(results, 0, 3, resultArray); +} diff --git a/classpath/java/util/zip/DataFormatException.java b/classpath/java/util/zip/DataFormatException.java new file mode 100644 index 0000000000..fe5a9fd99d --- /dev/null +++ b/classpath/java/util/zip/DataFormatException.java @@ -0,0 +1,11 @@ +package java.util.zip; + +public class DataFormatException extends Exception { + public DataFormatException(String s) { + super(s); + } + + public DataFormatException() { + super(); + } +} diff --git a/classpath/java/util/zip/Inflater.java b/classpath/java/util/zip/Inflater.java new file mode 100644 index 0000000000..cb21dda892 --- /dev/null +++ b/classpath/java/util/zip/Inflater.java @@ -0,0 +1,110 @@ +package java.util.zip; + +public class Inflater { + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + + static { + System.loadLibrary("natives"); + } + + private long peer; + private byte[] input; + private int offset; + private int length; + private boolean needDictionary; + private boolean finished; + + public Inflater(boolean nowrap) { + peer = make(nowrap); + } + + public Inflater() { + this(false); + } + + private void check() { + if (peer == 0) { + throw new IllegalStateException(); + } + } + + private static native long make(boolean nowrap); + + public boolean finished() { + return finished; + } + + public boolean needsDictionary() { + return needDictionary; + } + + public boolean needsInput() { + return getRemaining() == 0; + } + + public int getRemaining() { + return length; + } + + public void setInput(byte[] input, int offset, int length) { + this.input = input; + this.offset = offset; + this.length = length; + } + + public int inflate(byte[] output, int offset, int length) + throws DataFormatException + { + final int zlibResult = 0; + final int inputCount = 1; + final int outputCount = 2; + + if (peer == 0) { + throw new IllegalStateException(); + } + + if (input == null || output == null) { + throw new NullPointerException(); + } + + int[] results = new int[3]; + inflate(peer, input, this.offset, this.length, + output, offset, length, results); + + if (results[zlibResult] < 0) { + throw new DataFormatException(); + } + + switch (results[zlibResult]) { + case Z_NEED_DICT: + needDictionary = true; + break; + + case Z_STREAM_END: + finished = true; + break; + } + + this.offset += results[inputCount]; + this.length -= results[inputCount]; + + return results[outputCount]; + } + + private static native void inflate + (long peer, + byte[] input, int inputOffset, int inputLength, + byte[] output, int outputOffset, int outputLength, + int[] results); + + public void dispose() { + if (peer != 0) { + dispose(peer); + peer = 0; + } + } + + private static native void dispose(long peer); +} diff --git a/classpath/java/util/zip/InflaterInputStream.java b/classpath/java/util/zip/InflaterInputStream.java index 5288fa2596..fc2ec82942 100644 --- a/classpath/java/util/zip/InflaterInputStream.java +++ b/classpath/java/util/zip/InflaterInputStream.java @@ -2,19 +2,64 @@ package java.util.zip; import java.io.InputStream; import java.io.IOException; +import java.io.EOFException; public class InflaterInputStream extends InputStream { private final InputStream in; + private final Inflater inflater; + private final byte[] buffer; + + public InflaterInputStream(InputStream in, Inflater inflater, int bufferSize) + { + this.in = in; + this.inflater = inflater; + this.buffer = new byte[bufferSize]; + } + + public InflaterInputStream(InputStream in, Inflater inflater) { + this(in, inflater, 4 * 1024); + } public InflaterInputStream(InputStream in) { - this.in = in; + this(in, new Inflater()); } public int read() throws IOException { - throw new IOException("not implemented"); + byte[] buffer = new byte[1]; + int c = read(buffer); + return (c < 0 ? c : (buffer[0] & 0xFF)); + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (inflater.finished()) { + return -1; + } + + while (true) { + if (inflater.needsInput()) { + int count = in.read(buffer); + if (count > 0) { + inflater.setInput(buffer, 0, count); + } else { + throw new EOFException(); + } + } + + try { + int count = inflater.inflate(b, offset, length); + if (count > 0) { + return count; + } else if (inflater.needsDictionary()) { + throw new IOException("missing dictionary"); + } + } catch (DataFormatException e) { + throw new IOException(e); + } + } } public void close() throws IOException { in.close(); + inflater.dispose(); } }