blob: 2f27f5cda11e22fbdbd1f2b5eae6648d126ff43c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Deflater"
#include "JniConstants.h"
#include "ScopedPrimitiveArray.h"
#include "zip.h"
static void Deflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, int off, int len, jlong handle) {
toNativeZipStream(handle)->setDictionary(env, dict, off, len, false);
}
static jlong Deflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
return toNativeZipStream(handle)->stream.total_in;
}
static jlong Deflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
return toNativeZipStream(handle)->stream.total_out;
}
static jint Deflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
return toNativeZipStream(handle)->stream.adler;
}
static jlong Deflater_createStream(JNIEnv * env, jobject, jint level, jint strategy, jboolean noHeader) {
UniquePtr<NativeZipStream> jstream(new NativeZipStream);
if (jstream.get() == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return -1;
}
/*
* See zlib.h for documentation of the deflateInit2 windowBits and memLevel parameters.
*
* zconf.h says the "requirements for deflate are (in bytes):
* (1 << (windowBits+2)) + (1 << (memLevel+9))
* that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
* plus a few kilobytes for small objects."
*/
// TODO: should we just use DEF_WBITS (15) and DEF_MEM_LEVEL (8) now?
int windowBits = noHeader ? -12 : 12; // Was 15, made it 12 to reduce memory consumption. Use MAX_WBITS for fastest.
int memLevel = 5; // Was 9 (MAX_MEM_LEVEL), made it 5 to reduce memory consumption. Might result
// in out-of-memory problems according to some web pages.
int err = deflateInit2(&jstream->stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
if (err != Z_OK) {
throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
return -1;
}
return reinterpret_cast<uintptr_t>(jstream.release());
}
static void Deflater_setInputImpl(JNIEnv* env, jobject, jbyteArray buf, jint off, jint len, jlong handle) {
toNativeZipStream(handle)->setInput(env, buf, off, len);
}
static jint Deflater_deflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int off, int len, jlong handle, int flushStyle) {
NativeZipStream* stream = toNativeZipStream(handle);
ScopedByteArrayRW out(env, buf);
if (out.get() == NULL) {
return -1;
}
stream->stream.next_out = reinterpret_cast<Bytef*>(out.get() + off);
stream->stream.avail_out = len;
Bytef* initialNextIn = stream->stream.next_in;
Bytef* initialNextOut = stream->stream.next_out;
int err = deflate(&stream->stream, flushStyle);
switch (err) {
case Z_OK:
break;
case Z_STREAM_END:
static jfieldID finished = env->GetFieldID(JniConstants::deflaterClass, "finished", "Z");
env->SetBooleanField(recv, finished, JNI_TRUE);
break;
case Z_BUF_ERROR:
// zlib reports this "if no progress is possible (for example avail_in or avail_out was
// zero) ... Z_BUF_ERROR is not fatal, and deflate() can be called again with more
// input and more output space to continue compressing".
break;
default:
throwExceptionForZlibError(env, "java/util/zip/DataFormatException", err);
return -1;
}
jint bytesRead = stream->stream.next_in - initialNextIn;
jint bytesWritten = stream->stream.next_out - initialNextOut;
static jfieldID inReadField = env->GetFieldID(JniConstants::deflaterClass, "inRead", "I");
jint inReadValue = env->GetIntField(recv, inReadField);
inReadValue += bytesRead;
env->SetIntField(recv, inReadField, inReadValue);
return bytesWritten;
}
static void Deflater_endImpl(JNIEnv*, jobject, jlong handle) {
NativeZipStream* stream = toNativeZipStream(handle);
deflateEnd(&stream->stream);
delete stream;
}
static void Deflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
NativeZipStream* stream = toNativeZipStream(handle);
int err = deflateReset(&stream->stream);
if (err != Z_OK) {
throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err);
}
}
static void Deflater_setLevelsImpl(JNIEnv* env, jobject, int level, int strategy, jlong handle) {
NativeZipStream* stream = toNativeZipStream(handle);
// The deflateParams documentation says that avail_out must never be 0 because it may be
// necessary to flush, but the Java API ensures that we only get here if there's nothing
// to flush. To be on the safe side, make sure that we're not pointing to a no longer valid
// buffer.
stream->stream.next_out = reinterpret_cast<Bytef*>(NULL);
stream->stream.avail_out = 0;
int err = deflateParams(&stream->stream, level, strategy);
if (err != Z_OK) {
throwExceptionForZlibError(env, "java/lang/IllegalStateException", err);
}
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Deflater, createStream, "(IIZ)J"),
NATIVE_METHOD(Deflater, deflateImpl, "([BIIJI)I"),
NATIVE_METHOD(Deflater, endImpl, "(J)V"),
NATIVE_METHOD(Deflater, getAdlerImpl, "(J)I"),
NATIVE_METHOD(Deflater, getTotalInImpl, "(J)J"),
NATIVE_METHOD(Deflater, getTotalOutImpl, "(J)J"),
NATIVE_METHOD(Deflater, resetImpl, "(J)V"),
NATIVE_METHOD(Deflater, setDictionaryImpl, "([BIIJ)V"),
NATIVE_METHOD(Deflater, setInputImpl, "([BIIJ)V"),
NATIVE_METHOD(Deflater, setLevelsImpl, "(IIJ)V"),
};
int register_java_util_zip_Deflater(JNIEnv* env) {
return jniRegisterNativeMethods(env, "java/util/zip/Deflater", gMethods, NELEM(gMethods));
}