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