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 "ZipUtilities.h"
23 #include <errno.h>
24
Inflater_createStream(JNIEnv * env,jobject,jboolean noHeader)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
Inflater_setInputImpl(JNIEnv * env,jobject,jbyteArray buf,jint off,jint len,jlong handle)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
Inflater_setFileInputImpl(JNIEnv * env,jobject,jobject javaFileDescriptor,jlong off,jint len,jlong handle)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
Inflater_inflateImpl(JNIEnv * env,jobject recv,jbyteArray buf,int off,int len,jlong handle)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
Inflater_getAdlerImpl(JNIEnv *,jobject,jlong handle)130 static jint Inflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
131 return toNativeZipStream(handle)->stream.adler;
132 }
133
Inflater_endImpl(JNIEnv *,jobject,jlong handle)134 static void Inflater_endImpl(JNIEnv*, jobject, jlong handle) {
135 NativeZipStream* stream = toNativeZipStream(handle);
136 inflateEnd(&stream->stream);
137 delete stream;
138 }
139
Inflater_setDictionaryImpl(JNIEnv * env,jobject,jbyteArray dict,int off,int len,jlong handle)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
Inflater_resetImpl(JNIEnv * env,jobject,jlong handle)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
Inflater_getTotalOutImpl(JNIEnv *,jobject,jlong handle)151 static jlong Inflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
152 return toNativeZipStream(handle)->stream.total_out;
153 }
154
Inflater_getTotalInImpl(JNIEnv *,jobject,jlong handle)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 };
register_java_util_zip_Inflater(JNIEnv * env)171 void register_java_util_zip_Inflater(JNIEnv* env) {
172 jniRegisterNativeMethods(env, "java/util/zip/Inflater", gMethods, NELEM(gMethods));
173 }
174