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