Home | History | Annotate | Download | only in native
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #define LOG_TAG "Inflater"
     19 
     20 #include "JniConstants.h"
     21 #include "ScopedPrimitiveArray.h"
     22 #include "zip.h"
     23 #include <errno.h>
     24 
     25 static jlong Inflater_createStream(JNIEnv* env, jobject, jboolean noHeader) {
     26     UniquePtr<NativeZipStream> jstream(new NativeZipStream);
     27     if (jstream.get() == NULL) {
     28         jniThrowOutOfMemoryError(env, NULL);
     29         return -1;
     30     }
     31     jstream->stream.adler = 1;
     32 
     33     /*
     34      * See zlib.h for documentation of the inflateInit2 windowBits parameter.
     35      *
     36      * zconf.h says the "requirements for inflate are (in bytes) 1 << windowBits
     37      * that is, 32K for windowBits=15 (default value) plus a few kilobytes
     38      * for small objects." This means that we can happily use the default
     39      * here without worrying about memory consumption.
     40      */
     41     int err = inflateInit2(&jstream->stream, noHeader ? -DEF_WBITS : DEF_WBITS);
     42     if (err != Z_OK) {
     43         throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
     44         return -1;
     45     }
     46     return reinterpret_cast<uintptr_t>(jstream.release());
     47 }
     48 
     49 static void Inflater_setInputImpl(JNIEnv* env, jobject, jbyteArray buf, jint off, jint len, jlong handle) {
     50     toNativeZipStream(handle)->setInput(env, buf, off, len);
     51 }
     52 
     53 static jint Inflater_setFileInputImpl(JNIEnv* env, jobject, jobject javaFileDescriptor, jlong off, jint len, jlong handle) {
     54     NativeZipStream* stream = toNativeZipStream(handle);
     55 
     56     // We reuse the existing native buffer if it's large enough.
     57     // TODO: benchmark.
     58     if (stream->inCap < len) {
     59         stream->setInput(env, NULL, 0, len);
     60     } else {
     61         stream->stream.next_in = reinterpret_cast<Bytef*>(&stream->input[0]);
     62         stream->stream.avail_in = len;
     63     }
     64 
     65     // As an Android-specific optimization, we read directly onto the native heap.
     66     // The original code used Java to read onto the Java heap and then called setInput(byte[]).
     67     // TODO: benchmark.
     68     int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
     69     int rc = TEMP_FAILURE_RETRY(lseek(fd, off, SEEK_SET));
     70     if (rc == -1) {
     71         jniThrowIOException(env, errno);
     72         return 0;
     73     }
     74     jint totalByteCount = 0;
     75     Bytef* dst = reinterpret_cast<Bytef*>(&stream->input[0]);
     76     ssize_t byteCount;
     77     while ((byteCount = TEMP_FAILURE_RETRY(read(fd, dst, len))) > 0) {
     78         dst += byteCount;
     79         len -= byteCount;
     80         totalByteCount += byteCount;
     81     }
     82     if (byteCount == -1) {
     83         jniThrowIOException(env, errno);
     84         return 0;
     85     }
     86     return totalByteCount;
     87 }
     88 
     89 static jint Inflater_inflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int off, int len, jlong handle) {
     90     NativeZipStream* stream = toNativeZipStream(handle);
     91     ScopedByteArrayRW out(env, buf);
     92     if (out.get() == NULL) {
     93         return -1;
     94     }
     95     stream->stream.next_out = reinterpret_cast<Bytef*>(out.get() + off);
     96     stream->stream.avail_out = len;
     97 
     98     Bytef* initialNextIn = stream->stream.next_in;
     99     Bytef* initialNextOut = stream->stream.next_out;
    100 
    101     int err = inflate(&stream->stream, Z_SYNC_FLUSH);
    102     switch (err) {
    103     case Z_OK:
    104         break;
    105     case Z_NEED_DICT:
    106         static jfieldID needsDictionary = env->GetFieldID(JniConstants::inflaterClass, "needsDictionary", "Z");
    107         env->SetBooleanField(recv, needsDictionary, JNI_TRUE);
    108         break;
    109     case Z_STREAM_END:
    110         static jfieldID finished = env->GetFieldID(JniConstants::inflaterClass, "finished", "Z");
    111         env->SetBooleanField(recv, finished, JNI_TRUE);
    112         break;
    113     case Z_STREAM_ERROR:
    114         return 0;
    115     default:
    116         throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err);
    117         return -1;
    118     }
    119 
    120     jint bytesRead = stream->stream.next_in - initialNextIn;
    121     jint bytesWritten = stream->stream.next_out - initialNextOut;
    122 
    123     static jfieldID inReadField = env->GetFieldID(JniConstants::inflaterClass, "inRead", "I");
    124     jint inReadValue = env->GetIntField(recv, inReadField);
    125     inReadValue += bytesRead;
    126     env->SetIntField(recv, inReadField, inReadValue);
    127     return bytesWritten;
    128 }
    129 
    130 static jint Inflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
    131     return toNativeZipStream(handle)->stream.adler;
    132 }
    133 
    134 static void Inflater_endImpl(JNIEnv*, jobject, jlong handle) {
    135     NativeZipStream* stream = toNativeZipStream(handle);
    136     inflateEnd(&stream->stream);
    137     delete stream;
    138 }
    139 
    140 static void Inflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) {
    141     toNativeZipStream(handle)->setDictionary(env, dict, off, len, true);
    142 }
    143 
    144 static void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
    145     int err = inflateReset(&toNativeZipStream(handle)->stream);
    146     if (err != Z_OK) {
    147         throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
    148     }
    149 }
    150 
    151 static jlong Inflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
    152     return toNativeZipStream(handle)->stream.total_out;
    153 }
    154 
    155 static jlong Inflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
    156     return toNativeZipStream(handle)->stream.total_in;
    157 }
    158 
    159 static JNINativeMethod gMethods[] = {
    160     NATIVE_METHOD(Inflater, createStream, "(Z)J"),
    161     NATIVE_METHOD(Inflater, endImpl, "(J)V"),
    162     NATIVE_METHOD(Inflater, getAdlerImpl, "(J)I"),
    163     NATIVE_METHOD(Inflater, getTotalInImpl, "(J)J"),
    164     NATIVE_METHOD(Inflater, getTotalOutImpl, "(J)J"),
    165     NATIVE_METHOD(Inflater, inflateImpl, "([BIIJ)I"),
    166     NATIVE_METHOD(Inflater, resetImpl, "(J)V"),
    167     NATIVE_METHOD(Inflater, setDictionaryImpl, "([BIIJ)V"),
    168     NATIVE_METHOD(Inflater, setFileInputImpl, "(Ljava/io/FileDescriptor;JIJ)I"),
    169     NATIVE_METHOD(Inflater, setInputImpl, "([BIIJ)V"),
    170 };
    171 int register_java_util_zip_Inflater(JNIEnv* env) {
    172     return jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
    173 }
    174