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 "Deflater"
19abf945fb9ce99d8c2769ac5b2691b2732fa59887Elliott Hughes
20a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes#include "JniConstants.h"
21011ed31c4d057d973931fa81a09d8c576a72d82aKenny Root#include "JniException.h"
2299c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes#include "ScopedPrimitiveArray.h"
233aac4ddc4d17c07fa8b4908069d23d5401a77993Elliott Hughes#include "ZipUtilities.h"
24011ed31c4d057d973931fa81a09d8c576a72d82aKenny Root#include "zutil.h" // For DEF_WBITS and DEF_MEM_LEVEL.
2574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
2674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic void Deflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) {
2774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    toNativeZipStream(handle)->setDictionary(env, dict, off, len, false);
2874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
2974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
3044e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic jlong Deflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
3174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return toNativeZipStream(handle)->stream.total_in;
3274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
3374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
3444e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic jlong Deflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
3574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return toNativeZipStream(handle)->stream.total_out;
3674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
3774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
3844e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic jint Deflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
3974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return toNativeZipStream(handle)->stream.adler;
4074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
4174c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
4274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic jlong Deflater_createStream(JNIEnv * env, jobject, jint level, jint strategy, jboolean noHeader) {
4374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    UniquePtr<NativeZipStream> jstream(new NativeZipStream);
4474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (jstream.get() == NULL) {
4574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        jniThrowOutOfMemoryError(env, NULL);
4674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        return -1;
4774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
4874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
492d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    /*
502d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * See zlib.h for documentation of the deflateInit2 windowBits and memLevel parameters.
512d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     *
522d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * zconf.h says the "requirements for deflate are (in bytes):
532d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     *         (1 << (windowBits+2)) +  (1 << (memLevel+9))
542d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
552d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     * plus a few kilobytes for small objects."
562d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes     */
57909a18fd6628cee6718865a7b7bf2534ea25f5ecElliott Hughes    int windowBits = noHeader ? -DEF_WBITS : DEF_WBITS;
58909a18fd6628cee6718865a7b7bf2534ea25f5ecElliott Hughes    int memLevel = DEF_MEM_LEVEL;
592d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    int err = deflateInit2(&jstream->stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
6074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (err != Z_OK) {
61011ed31c4d057d973931fa81a09d8c576a72d82aKenny Root        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, jstream.get());
6274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        return -1;
6374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
6474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    return reinterpret_cast<uintptr_t>(jstream.release());
6574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
6674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
6774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic void Deflater_setInputImpl(JNIEnv* env, jobject, jbyteArray buf, jint off, jint len, jlong handle) {
6874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    toNativeZipStream(handle)->setInput(env, buf, off, len);
6974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
7074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
712d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughesstatic jint Deflater_deflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int off, int len, jlong handle, int flushStyle) {
7274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    NativeZipStream* stream = toNativeZipStream(handle);
730adb7b318dd5d67559d5b31b1ef3280dd72e1f5fElliott Hughes    ScopedByteArrayRW out(env, buf);
7499c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    if (out.get() == NULL) {
7574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes        return -1;
7674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
770adb7b318dd5d67559d5b31b1ef3280dd72e1f5fElliott Hughes    stream->stream.next_out = reinterpret_cast<Bytef*>(out.get() + off);
782d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    stream->stream.avail_out = len;
792d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes
80fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    Bytef* initialNextIn = stream->stream.next_in;
81fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    Bytef* initialNextOut = stream->stream.next_out;
822d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes
832d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    int err = deflate(&stream->stream, flushStyle);
842d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    switch (err) {
852d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    case Z_OK:
862d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        break;
872d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    case Z_STREAM_END:
882d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        static jfieldID finished = env->GetFieldID(JniConstants::deflaterClass, "finished", "Z");
892d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        env->SetBooleanField(recv, finished, JNI_TRUE);
902d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        break;
912d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    case Z_BUF_ERROR:
922d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        // zlib reports this "if no progress is possible (for example avail_in or avail_out was
932d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        // zero) ... Z_BUF_ERROR is not fatal, and deflate() can be called again with more
942d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        // input and more output space to continue compressing".
952d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        break;
962d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes    default:
97011ed31c4d057d973931fa81a09d8c576a72d82aKenny Root        throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err, stream);
982d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes        return -1;
9974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
1002d9c5fa8ce0182cd8c14736241b709fd50cab6f8Elliott Hughes
101fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    jint bytesRead = stream->stream.next_in - initialNextIn;
102fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    jint bytesWritten = stream->stream.next_out - initialNextOut;
103fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes
104fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    static jfieldID inReadField = env->GetFieldID(JniConstants::deflaterClass, "inRead", "I");
105fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    jint inReadValue = env->GetIntField(recv, inReadField);
106fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    inReadValue += bytesRead;
107fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    env->SetIntField(recv, inReadField, inReadValue);
108fa5cdaa79ff491dd5c05d72baad27f931cf2d08eElliott Hughes    return bytesWritten;
10974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
11074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
11144e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic void Deflater_endImpl(JNIEnv*, jobject, jlong handle) {
11274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    NativeZipStream* stream = toNativeZipStream(handle);
11374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    deflateEnd(&stream->stream);
11474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    delete stream;
11574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
11674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
11774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic void Deflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
11874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    NativeZipStream* stream = toNativeZipStream(handle);
11974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    int err = deflateReset(&stream->stream);
12074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (err != Z_OK) {
121011ed31c4d057d973931fa81a09d8c576a72d82aKenny Root        throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream);
12274c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
12374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
12474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
12574c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic void Deflater_setLevelsImpl(JNIEnv* env, jobject, int level, int strategy, jlong handle) {
12674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    NativeZipStream* stream = toNativeZipStream(handle);
127b7f0ced490fa5c5ad389c58b8584def84391f591Elliott Hughes    // The deflateParams documentation says that avail_out must never be 0 because it may be
128b7f0ced490fa5c5ad389c58b8584def84391f591Elliott Hughes    // necessary to flush, but the Java API ensures that we only get here if there's nothing
129b7f0ced490fa5c5ad389c58b8584def84391f591Elliott Hughes    // to flush. To be on the safe side, make sure that we're not pointing to a no longer valid
130b7f0ced490fa5c5ad389c58b8584def84391f591Elliott Hughes    // buffer.
131b7f0ced490fa5c5ad389c58b8584def84391f591Elliott Hughes    stream->stream.next_out = reinterpret_cast<Bytef*>(NULL);
132b7f0ced490fa5c5ad389c58b8584def84391f591Elliott Hughes    stream->stream.avail_out = 0;
13374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    int err = deflateParams(&stream->stream, level, strategy);
13474c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    if (err != Z_OK) {
135011ed31c4d057d973931fa81a09d8c576a72d82aKenny Root        throwExceptionForZlibError(env, "java/lang/IllegalStateException", err, stream);
13674c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes    }
13774c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
13874c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes
13974c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughesstatic JNINativeMethod gMethods[] = {
140e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, createStream, "(IIZ)J"),
141e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, deflateImpl, "([BIIJI)I"),
142e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, endImpl, "(J)V"),
143e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, getAdlerImpl, "(J)I"),
144e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, getTotalInImpl, "(J)J"),
145e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, getTotalOutImpl, "(J)J"),
146e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, resetImpl, "(J)V"),
147e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, setDictionaryImpl, "([BIIJ)V"),
148e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, setInputImpl, "([BIIJ)V"),
149e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(Deflater, setLevelsImpl, "(IIJ)V"),
15074c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes};
1517cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughesvoid register_java_util_zip_Deflater(JNIEnv* env) {
1527cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughes    jniRegisterNativeMethods(env, "java/util/zip/Deflater", gMethods, NELEM(gMethods));
15374c05e2a892f236c8648af7f4cfb2bcb483f267bElliott Hughes}
154