• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "JniException.h"
22 #include "ScopedPrimitiveArray.h"
23 #include "ZipUtilities.h"
24 #include "zutil.h" // For DEF_WBITS and DEF_MEM_LEVEL.
25 #include <errno.h>
26 
Inflater_createStream(JNIEnv * env,jobject,jboolean noHeader)27 static jlong Inflater_createStream(JNIEnv* env, jobject, jboolean noHeader) {
28     std::unique_ptr<NativeZipStream> jstream(new NativeZipStream);
29     if (jstream.get() == NULL) {
30         jniThrowOutOfMemoryError(env, NULL);
31         return -1;
32     }
33     jstream->stream.adler = 1;
34 
35     /*
36      * See zlib.h for documentation of the inflateInit2 windowBits parameter.
37      *
38      * zconf.h says the "requirements for inflate are (in bytes) 1 << windowBits
39      * that is, 32K for windowBits=15 (default value) plus a few kilobytes
40      * for small objects." This means that we can happily use the default
41      * here without worrying about memory consumption.
42      */
43     int err = inflateInit2(&jstream->stream, noHeader ? -DEF_WBITS : DEF_WBITS);
44     if (err != Z_OK) {
45         throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, jstream.get());
46         return -1;
47     }
48     return reinterpret_cast<uintptr_t>(jstream.release());
49 }
50 
Inflater_setInputImpl(JNIEnv * env,jobject,jbyteArray buf,jint off,jint len,jlong handle)51 static void Inflater_setInputImpl(JNIEnv* env, jobject, jbyteArray buf, jint off, jint len, jlong handle) {
52     toNativeZipStream(handle)->setInput(env, buf, off, len);
53 }
54 
Inflater_setFileInputImpl(JNIEnv * env,jobject,jobject javaFileDescriptor,jlong off,jint len,jlong handle)55 static jint Inflater_setFileInputImpl(JNIEnv* env, jobject, jobject javaFileDescriptor, jlong off, jint len, jlong handle) {
56     NativeZipStream* stream = toNativeZipStream(handle);
57 
58     // We reuse the existing native buffer if it's large enough.
59     // TODO: benchmark.
60     if (stream->inCap < len) {
61         stream->setInput(env, NULL, 0, len);
62     } else {
63         stream->stream.next_in = reinterpret_cast<Bytef*>(&stream->input[0]);
64         stream->stream.avail_in = len;
65     }
66 
67     // As an Android-specific optimization, we read directly onto the native heap.
68     // The original code used Java to read onto the Java heap and then called setInput(byte[]).
69     // TODO: benchmark.
70     int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
71     int rc = TEMP_FAILURE_RETRY(lseek(fd, off, SEEK_SET));
72     if (rc == -1) {
73         jniThrowIOException(env, errno);
74         return 0;
75     }
76     jint totalByteCount = 0;
77     Bytef* dst = reinterpret_cast<Bytef*>(&stream->input[0]);
78     ssize_t byteCount;
79     while ((byteCount = TEMP_FAILURE_RETRY(read(fd, dst, len))) > 0) {
80         dst += byteCount;
81         len -= byteCount;
82         totalByteCount += byteCount;
83     }
84     if (byteCount == -1) {
85         jniThrowIOException(env, errno);
86         return 0;
87     }
88     return totalByteCount;
89 }
90 
Inflater_inflateImpl(JNIEnv * env,jobject recv,jbyteArray buf,int off,int len,jlong handle)91 static jint Inflater_inflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int off, int len, jlong handle) {
92     NativeZipStream* stream = toNativeZipStream(handle);
93     ScopedByteArrayRW out(env, buf);
94     if (out.get() == NULL) {
95         return -1;
96     }
97     stream->stream.next_out = reinterpret_cast<Bytef*>(out.get() + off);
98     stream->stream.avail_out = len;
99 
100     Bytef* initialNextIn = stream->stream.next_in;
101     Bytef* initialNextOut = stream->stream.next_out;
102 
103     int err = inflate(&stream->stream, Z_SYNC_FLUSH);
104     switch (err) {
105     case Z_OK:
106         break;
107     case Z_NEED_DICT:
108         static jfieldID needsDictionary = env->GetFieldID(JniConstants::inflaterClass, "needsDictionary", "Z");
109         env->SetBooleanField(recv, needsDictionary, JNI_TRUE);
110         break;
111     case Z_STREAM_END:
112         static jfieldID finished = env->GetFieldID(JniConstants::inflaterClass, "finished", "Z");
113         env->SetBooleanField(recv, finished, JNI_TRUE);
114         break;
115     case Z_STREAM_ERROR:
116         return 0;
117     default:
118         throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err, stream);
119         return -1;
120     }
121 
122     jint bytesRead = stream->stream.next_in - initialNextIn;
123     jint bytesWritten = stream->stream.next_out - initialNextOut;
124 
125     stream->totalIn += bytesRead;
126     stream->totalOut += bytesWritten;
127 
128     static jfieldID inReadField = env->GetFieldID(JniConstants::inflaterClass, "inRead", "I");
129     jint inReadValue = env->GetIntField(recv, inReadField);
130     inReadValue += bytesRead;
131     env->SetIntField(recv, inReadField, inReadValue);
132     return bytesWritten;
133 }
134 
Inflater_getAdlerImpl(JNIEnv *,jobject,jlong handle)135 static jint Inflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
136     return toNativeZipStream(handle)->stream.adler;
137 }
138 
Inflater_endImpl(JNIEnv *,jobject,jlong handle)139 static void Inflater_endImpl(JNIEnv*, jobject, jlong handle) {
140     NativeZipStream* stream = toNativeZipStream(handle);
141     inflateEnd(&stream->stream);
142     delete stream;
143 }
144 
Inflater_setDictionaryImpl(JNIEnv * env,jobject,jbyteArray dict,int off,int len,jlong handle)145 static void Inflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) {
146     toNativeZipStream(handle)->setDictionary(env, dict, off, len, true);
147 }
148 
Inflater_resetImpl(JNIEnv * env,jobject,jlong handle)149 static void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
150     NativeZipStream* stream = toNativeZipStream(handle);
151     stream->totalIn = 0;
152     stream->totalOut = 0;
153     int err = inflateReset(&stream->stream);
154     if (err != Z_OK) {
155         throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream);
156     }
157 }
158 
Inflater_getTotalOutImpl(JNIEnv *,jobject,jlong handle)159 static jlong Inflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
160     return toNativeZipStream(handle)->totalOut;
161 }
162 
Inflater_getTotalInImpl(JNIEnv *,jobject,jlong handle)163 static jlong Inflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
164     return toNativeZipStream(handle)->totalIn;
165 }
166 
167 static JNINativeMethod gMethods[] = {
168     NATIVE_METHOD(Inflater, createStream, "(Z)J"),
169     NATIVE_METHOD(Inflater, endImpl, "(J)V"),
170     NATIVE_METHOD(Inflater, getAdlerImpl, "(J)I"),
171     NATIVE_METHOD(Inflater, getTotalInImpl, "(J)J"),
172     NATIVE_METHOD(Inflater, getTotalOutImpl, "(J)J"),
173     NATIVE_METHOD(Inflater, inflateImpl, "([BIIJ)I"),
174     NATIVE_METHOD(Inflater, resetImpl, "(J)V"),
175     NATIVE_METHOD(Inflater, setDictionaryImpl, "([BIIJ)V"),
176     NATIVE_METHOD(Inflater, setFileInputImpl, "(Ljava/io/FileDescriptor;JIJ)I"),
177     NATIVE_METHOD(Inflater, setInputImpl, "([BIIJ)V"),
178 };
register_java_util_zip_Inflater(JNIEnv * env)179 void register_java_util_zip_Inflater(JNIEnv* env) {
180     jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
181 }
182