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