Home | History | Annotate | Download | only in native
      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