174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes/*
274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * Licensed to the Apache Software Foundation (ASF) under one or more
374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * contributor license agreements.  See the NOTICE file distributed with
474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * this work for additional information regarding copyright ownership.
574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * The ASF licenses this file to You under the Apache License, Version 2.0
674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * (the "License"); you may not use this file except in compliance with
774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * the License.  You may obtain a copy of the License at
874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes *
974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes *     http://www.apache.org/licenses/LICENSE-2.0
1074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes *
1174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * Unless required by applicable law or agreed to in writing, software
1274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * distributed under the License is distributed on an "AS IS" BASIS,
1374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * See the License for the specific language governing permissions and
1574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes * limitations under the License.
1674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes */
1774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
18abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes#define LOG_TAG "Inflater"
19abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes
20a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes#include "JniConstants.h"
2199c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes#include "ScopedPrimitiveArray.h"
2274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes#include "zip.h"
23abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes#include <errno.h>
2474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
2574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic jlong Inflater_createStream(JNIEnv* env, jobject, jboolean noHeader) {
2674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    UniquePtr<NativeZipStream> jstream(new NativeZipStream);
2774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (jstream.get() == NULL) {
2874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        jniThrowOutOfMemoryError(env, NULL);
2974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        return -1;
3074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
3174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    jstream->stream.adler = 1;
3274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
3374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    /*
342d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * See zlib.h for documentation of the inflateInit2 windowBits parameter.
352d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     *
362d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * zconf.h says the "requirements for inflate are (in bytes) 1 << windowBits
372d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * that is, 32K for windowBits=15 (default value) plus a few kilobytes
382d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * for small objects." This means that we can happily use the default
392d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * here without worrying about memory consumption.
4074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes     */
412d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    int err = inflateInit2(&jstream->stream, noHeader ? -DEF_WBITS : DEF_WBITS);
4274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (err != Z_OK) {
4374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
4474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        return -1;
4574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
4674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return reinterpret_cast<uintptr_t>(jstream.release());
4774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
4874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
4974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic void Inflater_setInputImpl(JNIEnv* env, jobject, jbyteArray buf, jint off, jint len, jlong handle) {
5074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    toNativeZipStream(handle)->setInput(env, buf, off, len);
5174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
5274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
5374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic jint Inflater_setFileInputImpl(JNIEnv* env, jobject, jobject javaFileDescriptor, jlong off, jint len, jlong handle) {
5474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    NativeZipStream* stream = toNativeZipStream(handle);
55abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes
56abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    // We reuse the existing native buffer if it's large enough.
57abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    // TODO: benchmark.
5874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (stream->inCap < len) {
5974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        stream->setInput(env, NULL, 0, len);
60f27475b3c3fc4c95569b9a4045cae1d6e846354aJesse Wilson    } else {
61abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        stream->stream.next_in = reinterpret_cast<Bytef*>(&stream->input[0]);
62f27475b3c3fc4c95569b9a4045cae1d6e846354aJesse Wilson        stream->stream.avail_in = len;
6374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
64f27475b3c3fc4c95569b9a4045cae1d6e846354aJesse Wilson
65abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    // As an Android-specific optimization, we read directly onto the native heap.
66abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    // The original code used Java to read onto the Java heap and then called setInput(byte[]).
67abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    // TODO: benchmark.
6874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
69abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    int rc = TEMP_FAILURE_RETRY(lseek(fd, off, SEEK_SET));
70abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    if (rc == -1) {
71abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        jniThrowIOException(env, errno);
72abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        return 0;
73abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    }
74abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    jint totalByteCount = 0;
75abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    Bytef* dst = reinterpret_cast<Bytef*>(&stream->input[0]);
76abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    ssize_t byteCount;
77abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    while ((byteCount = TEMP_FAILURE_RETRY(read(fd, dst, len))) > 0) {
78abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        dst += byteCount;
79abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        len -= byteCount;
80abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        totalByteCount += byteCount;
81abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    }
82abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    if (byteCount == -1) {
83abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        jniThrowIOException(env, errno);
84abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes        return 0;
85abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    }
86abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes    return totalByteCount;
8774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
8874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
8974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic jint Inflater_inflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int off, int len, jlong handle) {
9074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    NativeZipStream* stream = toNativeZipStream(handle);
910adb7b318dd5d67559d5b31b1ef3280dd72e1f5fElliott Hughes    ScopedByteArrayRW out(env, buf);
9299c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    if (out.get() == NULL) {
9374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        return -1;
9474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
950adb7b318dd5d67559d5b31b1ef3280dd72e1f5fElliott Hughes    stream->stream.next_out = reinterpret_cast<Bytef*>(out.get() + off);
962d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    stream->stream.avail_out = len;
972d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes
982d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    Bytef* initialNextIn = stream->stream.next_in;
992d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    Bytef* initialNextOut = stream->stream.next_out;
1002d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes
10174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    int err = inflate(&stream->stream, Z_SYNC_FLUSH);
1022d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    switch (err) {
1032d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    case Z_OK:
1042d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        break;
1052d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    case Z_NEED_DICT:
1062d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        static jfieldID needsDictionary = env->GetFieldID(JniConstants::inflaterClass, "needsDictionary", "Z");
1072d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        env->SetBooleanField(recv, needsDictionary, JNI_TRUE);
1082d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        break;
1092d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    case Z_STREAM_END:
1102d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        static jfieldID finished = env->GetFieldID(JniConstants::inflaterClass, "finished", "Z");
1112d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        env->SetBooleanField(recv, finished, JNI_TRUE);
1122d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        break;
1132d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    case Z_STREAM_ERROR:
1142d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        return 0;
1152d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    default:
1162d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err);
1172d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        return -1;
11874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
11974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
1202d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    jint bytesRead = stream->stream.next_in - initialNextIn;
1212d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    jint bytesWritten = stream->stream.next_out - initialNextOut;
1222d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes
1232d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    static jfieldID inReadField = env->GetFieldID(JniConstants::inflaterClass, "inRead", "I");
1242d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    jint inReadValue = env->GetIntField(recv, inReadField);
1252d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    inReadValue += bytesRead;
1262d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    env->SetIntField(recv, inReadField, inReadValue);
1272d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    return bytesWritten;
12874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
12974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
13044e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic jint Inflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
13174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return toNativeZipStream(handle)->stream.adler;
13274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
13374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
13444e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic void Inflater_endImpl(JNIEnv*, jobject, jlong handle) {
13574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    NativeZipStream* stream = toNativeZipStream(handle);
13674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    inflateEnd(&stream->stream);
13774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    delete stream;
13874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
13974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
14074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic void Inflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) {
14174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    toNativeZipStream(handle)->setDictionary(env, dict, off, len, true);
14274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
14374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
14474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
14574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    int err = inflateReset(&toNativeZipStream(handle)->stream);
14674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (err != Z_OK) {
14774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
14874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
14974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
15074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
15144e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic jlong Inflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
15274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return toNativeZipStream(handle)->stream.total_out;
15374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
15474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
15544e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic jlong Inflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
15674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return toNativeZipStream(handle)->stream.total_in;
15774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
15874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
15974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic JNINativeMethod gMethods[] = {
160e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, createStream, "(Z)J"),
161e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, endImpl, "(J)V"),
162e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, getAdlerImpl, "(J)I"),
163e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, getTotalInImpl, "(J)J"),
164e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, getTotalOutImpl, "(J)J"),
165e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, inflateImpl, "([BIIJ)I"),
166e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, resetImpl, "(J)V"),
167e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, setDictionaryImpl, "([BIIJ)V"),
168e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, setFileInputImpl, "(Ljava/io/FileDescriptor;JIJ)I"),
169e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Inflater, setInputImpl, "([BIIJ)V"),
17074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes};
1717cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughesvoid register_java_util_zip_Inflater(JNIEnv* env) {
1727cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughes    jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
17374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
174