blob: a488eee22447f32df5049b861e5b0e1c7a62e295 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "zip.h"
static struct {
jfieldID inRead;
jfieldID finished;
jfieldID needsDictionary;
} gCachedFields;
/* Create a new stream . This stream cannot be used until it has been properly initialized. */
static jlong Inflater_createStream(JNIEnv* env, jobject, jboolean noHeader) {
UniquePtr<NativeZipStream> jstream(new NativeZipStream);
if (jstream.get() == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return -1;
}
jstream->stream.adler = 1;
/*
* In the range 8..15 for checked, or -8..-15 for unchecked inflate. Unchecked
* is appropriate for formats like zip that do their own validity checking.
*/
/* Window bits to use. 15 is fastest but consumes the most memory */
int wbits = 15; /*Use MAX for fastest */
if (noHeader) {
wbits = wbits / -1;
}
int err = inflateInit2(&jstream->stream, wbits);
if (err != Z_OK) {
throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
return -1;
}
return reinterpret_cast<uintptr_t>(jstream.release());
}
static void Inflater_setInputImpl(JNIEnv* env, jobject, jbyteArray buf, jint off, jint len, jlong handle) {
toNativeZipStream(handle)->setInput(env, buf, off, len);
}
static jint Inflater_setFileInputImpl(JNIEnv* env, jobject, jobject javaFileDescriptor, jlong off, jint len, jlong handle) {
NativeZipStream* stream = toNativeZipStream(handle);
if (stream->inCap < len) {
stream->setInput(env, NULL, 0, len);
}
// TODO: is it okay to be this sloppy about errors?
int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
lseek(fd, off, SEEK_SET);
return read(fd, &stream->input[0], len);
}
static jint Inflater_inflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int off, int len, jlong handle) {
jfieldID fid2 = 0;
/* We need to get the number of bytes already read */
jfieldID fid = gCachedFields.inRead;
jint inBytes = env->GetIntField(recv, fid);
NativeZipStream* stream = toNativeZipStream(handle);
stream->stream.avail_out = len;
jint sin = stream->stream.total_in;
jint sout = stream->stream.total_out;
jbyte* out = (jbyte*) env->GetPrimitiveArrayCritical(buf, 0);
if (out == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return -1;
}
stream->stream.next_out = (Bytef *) out + off;
int err = inflate(&stream->stream, Z_SYNC_FLUSH);
env->ReleasePrimitiveArrayCritical(buf, out, 0);
if (err != Z_OK) {
if (err == Z_STREAM_ERROR) {
return 0;
}
if (err == Z_STREAM_END || err == Z_NEED_DICT) {
env->SetIntField(recv, fid, (jint) stream->stream.total_in - sin + inBytes);
if (err == Z_STREAM_END) {
fid2 = gCachedFields.finished;
} else {
fid2 = gCachedFields.needsDictionary;
}
env->SetBooleanField(recv, fid2, JNI_TRUE);
return stream->stream.total_out - sout;
} else {
throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err);
return -1;
}
}
/* Need to update the number of input bytes read. Is there a better way
* (Maybe global the fid then delete when end is called)?
*/
env->SetIntField(recv, fid, (jint) stream->stream.total_in - sin + inBytes);
return stream->stream.total_out - sout;
}
static jint Inflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
return toNativeZipStream(handle)->stream.adler;
}
static void Inflater_endImpl(JNIEnv*, jobject, jlong handle) {
NativeZipStream* stream = toNativeZipStream(handle);
inflateEnd(&stream->stream);
delete stream;
}
static void Inflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) {
toNativeZipStream(handle)->setDictionary(env, dict, off, len, true);
}
static void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
int err = inflateReset(&toNativeZipStream(handle)->stream);
if (err != Z_OK) {
throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
}
}
static jlong Inflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
return toNativeZipStream(handle)->stream.total_out;
}
static jlong Inflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
return toNativeZipStream(handle)->stream.total_in;
}
static void Inflater_oneTimeInitialization(JNIEnv * env, jclass clazz) {
memset(&gCachedFields, 0, sizeof(gCachedFields));
gCachedFields.inRead = env->GetFieldID(clazz, "inRead", "I");
gCachedFields.finished = env->GetFieldID(clazz, "finished", "Z");
gCachedFields.needsDictionary = env->GetFieldID(clazz, "needsDictionary", "Z");
}
static JNINativeMethod gMethods[] = {
{ "createStream", "(Z)J", (void*) Inflater_createStream },
{ "setInputImpl", "([BIIJ)V", (void*) Inflater_setInputImpl },
{ "setFileInputImpl", "(Ljava/io/FileDescriptor;JIJ)I", (void*) Inflater_setFileInputImpl },
{ "inflateImpl", "([BIIJ)I", (void*) Inflater_inflateImpl },
{ "getAdlerImpl", "(J)I", (void*) Inflater_getAdlerImpl },
{ "endImpl", "(J)V", (void*) Inflater_endImpl },
{ "setDictionaryImpl", "([BIIJ)V", (void*) Inflater_setDictionaryImpl },
{ "resetImpl", "(J)V", (void*) Inflater_resetImpl },
{ "getTotalOutImpl", "(J)J", (void*) Inflater_getTotalOutImpl },
{ "getTotalInImpl", "(J)J", (void*) Inflater_getTotalInImpl },
{ "oneTimeInitialization", "()V", (void*) Inflater_oneTimeInitialization },
};
int register_java_util_zip_Inflater(JNIEnv* env) {
return jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
}