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 "Deflater" 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 26 static void Deflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) { 27 toNativeZipStream(handle)->setDictionary(env, dict, off, len, false); 28 } 29 30 static jlong Deflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) { 31 return toNativeZipStream(handle)->stream.total_in; 32 } 33 34 static jlong Deflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) { 35 return toNativeZipStream(handle)->stream.total_out; 36 } 37 38 static jint Deflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) { 39 return toNativeZipStream(handle)->stream.adler; 40 } 41 42 static jlong Deflater_createStream(JNIEnv * env, jobject, jint level, jint strategy, jboolean noHeader) { 43 UniquePtr<NativeZipStream> jstream(new NativeZipStream); 44 if (jstream.get() == NULL) { 45 jniThrowOutOfMemoryError(env, NULL); 46 return -1; 47 } 48 49 /* 50 * See zlib.h for documentation of the deflateInit2 windowBits and memLevel parameters. 51 * 52 * zconf.h says the "requirements for deflate are (in bytes): 53 * (1 << (windowBits+2)) + (1 << (memLevel+9)) 54 * that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) 55 * plus a few kilobytes for small objects." 56 */ 57 int windowBits = noHeader ? -DEF_WBITS : DEF_WBITS; 58 int memLevel = DEF_MEM_LEVEL; 59 int err = deflateInit2(&jstream->stream, level, Z_DEFLATED, windowBits, memLevel, strategy); 60 if (err != Z_OK) { 61 throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, jstream.get()); 62 return -1; 63 } 64 return reinterpret_cast<uintptr_t>(jstream.release()); 65 } 66 67 static void Deflater_setInputImpl(JNIEnv* env, jobject, jbyteArray buf, jint off, jint len, jlong handle) { 68 toNativeZipStream(handle)->setInput(env, buf, off, len); 69 } 70 71 static jint Deflater_deflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int off, int len, jlong handle, int flushStyle) { 72 NativeZipStream* stream = toNativeZipStream(handle); 73 ScopedByteArrayRW out(env, buf); 74 if (out.get() == NULL) { 75 return -1; 76 } 77 stream->stream.next_out = reinterpret_cast<Bytef*>(out.get() + off); 78 stream->stream.avail_out = len; 79 80 Bytef* initialNextIn = stream->stream.next_in; 81 Bytef* initialNextOut = stream->stream.next_out; 82 83 int err = deflate(&stream->stream, flushStyle); 84 switch (err) { 85 case Z_OK: 86 break; 87 case Z_STREAM_END: 88 static jfieldID finished = env->GetFieldID(JniConstants::deflaterClass, "finished", "Z"); 89 env->SetBooleanField(recv, finished, JNI_TRUE); 90 break; 91 case Z_BUF_ERROR: 92 // zlib reports this "if no progress is possible (for example avail_in or avail_out was 93 // zero) ... Z_BUF_ERROR is not fatal, and deflate() can be called again with more 94 // input and more output space to continue compressing". 95 break; 96 default: 97 throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err, stream); 98 return -1; 99 } 100 101 jint bytesRead = stream->stream.next_in - initialNextIn; 102 jint bytesWritten = stream->stream.next_out - initialNextOut; 103 104 static jfieldID inReadField = env->GetFieldID(JniConstants::deflaterClass, "inRead", "I"); 105 jint inReadValue = env->GetIntField(recv, inReadField); 106 inReadValue += bytesRead; 107 env->SetIntField(recv, inReadField, inReadValue); 108 return bytesWritten; 109 } 110 111 static void Deflater_endImpl(JNIEnv*, jobject, jlong handle) { 112 NativeZipStream* stream = toNativeZipStream(handle); 113 deflateEnd(&stream->stream); 114 delete stream; 115 } 116 117 static void Deflater_resetImpl(JNIEnv* env, jobject, jlong handle) { 118 NativeZipStream* stream = toNativeZipStream(handle); 119 int err = deflateReset(&stream->stream); 120 if (err != Z_OK) { 121 throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream); 122 } 123 } 124 125 static void Deflater_setLevelsImpl(JNIEnv* env, jobject, int level, int strategy, jlong handle) { 126 NativeZipStream* stream = toNativeZipStream(handle); 127 // The deflateParams documentation says that avail_out must never be 0 because it may be 128 // necessary to flush, but the Java API ensures that we only get here if there's nothing 129 // to flush. To be on the safe side, make sure that we're not pointing to a no longer valid 130 // buffer. 131 stream->stream.next_out = reinterpret_cast<Bytef*>(NULL); 132 stream->stream.avail_out = 0; 133 int err = deflateParams(&stream->stream, level, strategy); 134 if (err != Z_OK) { 135 throwExceptionForZlibError(env, "java/lang/IllegalStateException", err, stream); 136 } 137 } 138 139 static JNINativeMethod gMethods[] = { 140 NATIVE_METHOD(Deflater, createStream, "(IIZ)J"), 141 NATIVE_METHOD(Deflater, deflateImpl, "([BIIJI)I"), 142 NATIVE_METHOD(Deflater, endImpl, "(J)V"), 143 NATIVE_METHOD(Deflater, getAdlerImpl, "(J)I"), 144 NATIVE_METHOD(Deflater, getTotalInImpl, "(J)J"), 145 NATIVE_METHOD(Deflater, getTotalOutImpl, "(J)J"), 146 NATIVE_METHOD(Deflater, resetImpl, "(J)V"), 147 NATIVE_METHOD(Deflater, setDictionaryImpl, "([BIIJ)V"), 148 NATIVE_METHOD(Deflater, setInputImpl, "([BIIJ)V"), 149 NATIVE_METHOD(Deflater, setLevelsImpl, "(IIJ)V"), 150 }; 151 void register_java_util_zip_Deflater(JNIEnv* env) { 152 jniRegisterNativeMethods(env, "java/util/zip/Deflater", gMethods, NELEM(gMethods)); 153 } 154