Subject: [PATCH] [Huawei]support KAE-zip

---
 jdk/make/CompileLaunchers.gmk                 | 34 ++++---
 jdk/make/lib/CoreLibraries.gmk                | 71 ++++++++++++++
 jdk/make/mapfiles/libzip/mapfile-vers         |  3 +
 .../share/classes/java/util/zip/Deflater.java | 10 +-
 .../java/util/zip/GZIPInputStream.java        | 14 +++
 .../java/util/zip/GZIPOutputStream.java       | 20 ++++
 .../share/classes/java/util/zip/Inflater.java | 14 ++-
 .../java/util/zip/InflaterOutputStream.java   | 24 ++---
 jdk/src/share/lib/security/java.policy        |  2 +
 jdk/src/share/native/java/util/zip/Deflater.c | 37 +++++++
 jdk/src/share/native/java/util/zip/Inflater.c | 98 +++++++++++++++++++
 .../java/util/zip/DeflateIn_InflateOut.java   | 74 +++++++++++++-
 12 files changed, 367 insertions(+), 34 deletions(-)

diff --git a/jdk/make/CompileLaunchers.gmk b/jdk/make/CompileLaunchers.gmk
index de1ea9f4b..513e2ee1b 100644
--- a/jdk/make/CompileLaunchers.gmk
+++ b/jdk/make/CompileLaunchers.gmk
@@ -474,17 +474,29 @@ ifeq ($(USE_EXTERNAL_LIBZ), true)
   UNPACKEXE_ZIPOBJS := -lz
 else
   UNPACKEXE_CFLAGS := -I$(JDK_TOPDIR)/src/share/native/java/util/zip/zlib
-  UNPACKEXE_ZIPOBJS := $(JDK_OUTPUTDIR)/objs/libzip/zcrc32$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/deflate$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/trees$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/zadler32$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/compress$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/zutil$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/inflate$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/infback$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/inftrees$(OBJ_SUFFIX) \
-      $(JDK_OUTPUTDIR)/objs/libzip/inffast$(OBJ_SUFFIX)
-
+  ifeq ($(OPENJDK_TARGET_OS), windows)
+    UNPACKEXE_ZIPOBJS := $(JDK_OUTPUTDIR)/objs/libzip/zcrc32$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/deflate$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/trees$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/zadler32$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/compress$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/zutil$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/inflate$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/infback$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/inftrees$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libzip/inffast$(OBJ_SUFFIX)
+  else
+    UNPACKEXE_ZIPOBJS := $(JDK_OUTPUTDIR)/objs/libz/zcrc32$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/deflate$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/trees$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/zadler32$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/compress$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/zutil$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/inflate$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/infback$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/inftrees$(OBJ_SUFFIX) \
+        $(JDK_OUTPUTDIR)/objs/libz/inffast$(OBJ_SUFFIX)
+  endif
 endif
 
 UNPACKEXE_LANG := C
diff --git a/jdk/make/lib/CoreLibraries.gmk b/jdk/make/lib/CoreLibraries.gmk
index d1cd1d3de..1af991693 100644
--- a/jdk/make/lib/CoreLibraries.gmk
+++ b/jdk/make/lib/CoreLibraries.gmk
@@ -261,11 +261,76 @@ $(BUILD_LIBJAVA): $(BUILD_LIBFDLIBM)
 
 ##########################################################################################
 
+ifneq ($(USE_EXTERNAL_LIBZ), true)
+  ifneq ($(OPENJDK_TARGET_OS), windows)
+    BUILD_LIBZ_EXCLUDES :=
+
+    ZLIB_CPPFLAGS := -I$(JDK_TOPDIR)/src/share/native/java/util/zip/zlib
+    ifeq ($(OPENJDK_TARGET_OS), macosx)
+      ZLIB_CPPFLAGS += -DHAVE_UNISTD_H
+    endif
+
+    BUILD_LIBZ_REORDER :=
+    ifeq ($(OPENJDK_TARGET_OS), solaris)
+      ifneq ($(OPENJDK_TARGET_CPU), x86_64)
+        BUILD_LIBZ_REORDER := $(JDK_TOPDIR)/make/mapfiles/libzip/reorder-$(OPENJDK_TARGET_CPU)
+      endif
+    endif
+
+    ifeq ($(LIBZ_CAN_USE_MMAP), true)
+      BUILD_LIBZ_MMAP := -DUSE_MMAP
+    endif
+
+    $(eval $(call SetupNativeCompilation,BUILD_LIBZ, \
+        LIBRARY := z, \
+        OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
+        LANG := C, \
+        OPTIMIZATION := LOW, \
+        SRC := $(JDK_TOPDIR)/src/share/native/java/util/zip/zlib, \
+        EXCLUDES := $(LIBZ_EXCLUDES), \
+        CFLAGS := $(CFLAGS_JDKLIB) \
+            $(ZLIB_CPPFLAGS) \
+            -I$(JDK_TOPDIR)/src/share/native/java/io \
+            -I$(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/java/io, \
+        CFLAGS_posix := $(BUILD_LIBZ_MMAP) -UDEBUG, \
+        MAPFILE := , \
+        REORDER := $(BUILD_LIBZ_REORDER), \
+        LDFLAGS := $(LDFLAGS_JDKLIB) \
+            $(call SET_SHARED_LIBRARY_ORIGIN) \
+            $(EXPORT_Z_FUNCS), \
+        LDFLAGS_windows := jvm.lib \
+            $(WIN_JAVA_LIB), \
+        LDFLAGS_SUFFIX_linux := , \
+        LDFLAGS_SUFFIX_solaris := , \
+        LDFLAGS_SUFFIX_aix := ,\
+        LDFLAGS_SUFFIX_macosx := , \
+        VERSIONINFO_RESOURCE := $(JDK_TOPDIR)/src/windows/resource/version.rc, \
+        RC_FLAGS := $(RC_FLAGS) \
+            -D "JDK_FNAME=z.dll" \
+            -D "JDK_INTERNAL_NAME=z" \
+            -D "JDK_FTYPE=0x2L", \
+        OBJECT_DIR := $(JDK_OUTPUTDIR)/objs/libz, \
+        DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES)))
+
+
+    $(BUILD_LIBZ): $(BUILD_LIBJAVA)
+
+    BUILD_LIBRARIES += $(BUILD_LIBZ)
+  endif
+endif
+
+##########################################################################################
+
 BUILD_LIBZIP_EXCLUDES :=
 ifeq ($(USE_EXTERNAL_LIBZ), true)
+  BUILD_LIBZIP_SRC :=
   LIBZ := -lz
   LIBZIP_EXCLUDES += zlib
 else
+  ifneq ($(OPENJDK_TARGET_OS), windows)
+    BUILD_LIBZIP_SRC := Adler32.c CRC32.c Deflater.c Inflater.c zip_util.c ZipFile.c
+    LIBZ := -lz
+  endif
   ZLIB_CPPFLAGS := -I$(JDK_TOPDIR)/src/share/native/java/util/zip/zlib
   ifeq ($(OPENJDK_TARGET_OS), macosx)
     ZLIB_CPPFLAGS += -DHAVE_UNISTD_H
@@ -289,6 +354,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBZIP, \
     LANG := C, \
     OPTIMIZATION := LOW, \
     SRC := $(JDK_TOPDIR)/src/share/native/java/util/zip, \
+    INCLUDE_FILES := $(BUILD_LIBZIP_SRC), \
     EXCLUDES := $(LIBZIP_EXCLUDES), \
     CFLAGS := $(CFLAGS_JDKLIB) \
         $(ZLIB_CPPFLAGS) \
@@ -315,9 +381,14 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBZIP, \
     OBJECT_DIR := $(JDK_OUTPUTDIR)/objs/libzip, \
     DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES)))
 
+ifneq ($(USE_EXTERNAL_LIBZ), true)
+  LIBZ :=
+endif
 
 $(BUILD_LIBZIP): $(BUILD_LIBJAVA)
 
+$(BUILD_LIBZIP): $(BUILD_LIBZ)
+
 BUILD_LIBRARIES += $(BUILD_LIBZIP)
 
 ##########################################################################################
diff --git a/jdk/make/mapfiles/libzip/mapfile-vers b/jdk/make/mapfiles/libzip/mapfile-vers
index 5d33990c3..5c6d27d0d 100644
--- a/jdk/make/mapfiles/libzip/mapfile-vers
+++ b/jdk/make/mapfiles/libzip/mapfile-vers
@@ -38,13 +38,16 @@ SUNWprivate_1.1 {
 		Java_java_util_zip_Deflater_end;
 		Java_java_util_zip_Deflater_getAdler;
 		Java_java_util_zip_Deflater_init;
+		Java_java_util_zip_Deflater_initKae;
 		Java_java_util_zip_Deflater_initIDs;
 		Java_java_util_zip_Deflater_reset;
 		Java_java_util_zip_Deflater_setDictionary;
 		Java_java_util_zip_Inflater_end;
 		Java_java_util_zip_Inflater_getAdler;
 		Java_java_util_zip_Inflater_inflateBytes;
+		Java_java_util_zip_Inflater_inflateBytesKAE;
 		Java_java_util_zip_Inflater_init;
+		Java_java_util_zip_Inflater_initKae;
 		Java_java_util_zip_Inflater_initIDs;
 		Java_java_util_zip_Inflater_reset;
 		Java_java_util_zip_Inflater_setDictionary;
diff --git a/jdk/src/share/classes/java/util/zip/Deflater.java b/jdk/src/share/classes/java/util/zip/Deflater.java
index bffa397d9..a4ea40cf8 100644
--- a/jdk/src/share/classes/java/util/zip/Deflater.java
+++ b/jdk/src/share/classes/java/util/zip/Deflater.java
@@ -81,6 +81,7 @@ class Deflater {
     private boolean finish, finished;
     private long bytesRead;
     private long bytesWritten;
+    private boolean defalterUseKae;
 
     /**
      * Compression method for the deflate algorithm (the only one currently
@@ -168,7 +169,13 @@ class Deflater {
     public Deflater(int level, boolean nowrap) {
         this.level = level;
         this.strategy = DEFAULT_STRATEGY;
-        this.zsRef = new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap));
+        if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) &&
+            ("aarch64".equals(System.getProperty("os.arch")))) {
+            this.defalterUseKae = true;
+        }
+        this.zsRef = defalterUseKae ?
+            new ZStreamRef(initKae(level, DEFAULT_STRATEGY)) :
+            new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap));
     }
 
     /**
@@ -571,6 +578,7 @@ class Deflater {
 
     private static native void initIDs();
     private native static long init(int level, int strategy, boolean nowrap);
+    private native static long initKae(int level, int strategy);
     private native static void setDictionary(long addr, byte[] b, int off, int len);
     private native int deflateBytes(long addr, byte[] b, int off, int len,
                                     int flush);
diff --git a/jdk/src/share/classes/java/util/zip/GZIPInputStream.java b/jdk/src/share/classes/java/util/zip/GZIPInputStream.java
index 0d57e8ab3..7fb753729 100644
--- a/jdk/src/share/classes/java/util/zip/GZIPInputStream.java
+++ b/jdk/src/share/classes/java/util/zip/GZIPInputStream.java
@@ -54,6 +54,11 @@ class GZIPInputStream extends InflaterInputStream {
 
     private boolean closed = false;
 
+    /*
+     * GZIP use KAE.
+     */
+    private boolean gzipUseKae = false;
+
     /**
      * Check to make sure that this stream has not been closed
      */
@@ -76,6 +81,12 @@ class GZIPInputStream extends InflaterInputStream {
     public GZIPInputStream(InputStream in, int size) throws IOException {
         super(in, new Inflater(true), size);
         usesDefaultInflater = true;
+        if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) &&
+            ("aarch64".equals(System.getProperty("os.arch")))) {
+            gzipUseKae = true;
+        }
+        // file header will be readed by kae zlib when use kae
+        if (gzipUseKae) return;
         readHeader(in);
     }
 
@@ -209,6 +220,9 @@ class GZIPInputStream extends InflaterInputStream {
      * data set)
      */
     private boolean readTrailer() throws IOException {
+        // file trailer will be readed by kae zlib when use kae
+        if (gzipUseKae) return true;
+
         InputStream in = this.in;
         int n = inf.getRemaining();
         if (n > 0) {
diff --git a/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java b/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java
index 1c3f8592e..0f0be98bb 100644
--- a/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java
+++ b/jdk/src/share/classes/java/util/zip/GZIPOutputStream.java
@@ -52,6 +52,11 @@ class GZIPOutputStream extends DeflaterOutputStream {
      */
     private final static int TRAILER_SIZE = 8;
 
+    /*
+     * GZIP use KAE.
+     */
+    private boolean gzipUseKae = false;
+
     /**
      * Creates a new output stream with the specified buffer size.
      *
@@ -91,6 +96,12 @@ class GZIPOutputStream extends DeflaterOutputStream {
               size,
               syncFlush);
         usesDefaultDeflater = true;
+        if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) &&
+            ("aarch64".equals(System.getProperty("os.arch")))) {
+            gzipUseKae = true;
+        }
+        // file header will be writed by kae zlib when use kae
+        if (gzipUseKae) return;
         writeHeader();
         crc.reset();
     }
@@ -160,6 +171,11 @@ class GZIPOutputStream extends DeflaterOutputStream {
                     int len = def.deflate(buf, 0, buf.length);
                     if (def.finished() && len <= buf.length - TRAILER_SIZE) {
                         // last deflater buffer. Fit trailer at the end
+                        // file trailer will be writed by kae zlib when use kae
+                        if (gzipUseKae) {
+                            out.write(buf, 0, len);
+                            return;
+                        }
                         writeTrailer(buf, len);
                         len = len + TRAILER_SIZE;
                         out.write(buf, 0, len);
@@ -168,6 +184,10 @@ class GZIPOutputStream extends DeflaterOutputStream {
                     if (len > 0)
                         out.write(buf, 0, len);
                 }
+                // file trailer will be writed by kae zlib when use kae
+                if (gzipUseKae) {
+                    return;
+                }
                 // if we can't fit the trailer at the end of the last
                 // deflater buffer, we write it separately
                 byte[] trailer = new byte[TRAILER_SIZE];
diff --git a/jdk/src/share/classes/java/util/zip/Inflater.java b/jdk/src/share/classes/java/util/zip/Inflater.java
index c1eefe122..42e90f525 100644
--- a/jdk/src/share/classes/java/util/zip/Inflater.java
+++ b/jdk/src/share/classes/java/util/zip/Inflater.java
@@ -80,6 +80,7 @@ class Inflater {
     private boolean needDict;
     private long bytesRead;
     private long bytesWritten;
+    private boolean inflaterUseKae;
 
     private static final byte[] defaultBuf = new byte[0];
 
@@ -100,7 +101,11 @@ class Inflater {
      * @param nowrap if true then support GZIP compatible compression
      */
     public Inflater(boolean nowrap) {
-        zsRef = new ZStreamRef(init(nowrap));
+        if (("true".equals(System.getProperty("GZIP_USE_KAE", "false"))) &&
+            ("aarch64".equals(System.getProperty("os.arch")))) {
+            inflaterUseKae = true;
+        }
+        zsRef = inflaterUseKae ? new ZStreamRef(initKae()): new ZStreamRef(init(nowrap));
     }
 
     /**
@@ -256,7 +261,9 @@ class Inflater {
         synchronized (zsRef) {
             ensureOpen();
             int thisLen = this.len;
-            int n = inflateBytes(zsRef.address(), b, off, len);
+            int n = this.inflaterUseKae ?
+                inflateBytesKAE(zsRef.address(), b, off, len) :
+                inflateBytes(zsRef.address(), b, off, len);
             bytesWritten += n;
             bytesRead += (thisLen - this.len);
             return n;
@@ -397,10 +404,13 @@ class Inflater {
 
     private native static void initIDs();
     private native static long init(boolean nowrap);
+    private native static long initKae();
     private native static void setDictionary(long addr, byte[] b, int off,
                                              int len);
     private native int inflateBytes(long addr, byte[] b, int off, int len)
             throws DataFormatException;
+    private native int inflateBytesKAE(long addr, byte[] b, int off, int len)
+            throws DataFormatException;  
     private native static int getAdler(long addr);
     private native static void reset(long addr);
     private native static void end(long addr);
diff --git a/jdk/src/share/classes/java/util/zip/InflaterOutputStream.java b/jdk/src/share/classes/java/util/zip/InflaterOutputStream.java
index 02900c741..62c6bb153 100644
--- a/jdk/src/share/classes/java/util/zip/InflaterOutputStream.java
+++ b/jdk/src/share/classes/java/util/zip/InflaterOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -236,16 +236,9 @@ public class InflaterOutputStream extends FilterOutputStream {
 
                 // Fill the decompressor buffer with output data
                 if (inf.needsInput()) {
-                    int part;
-
-                    if (len < 1) {
-                        break;
-                    }
-
-                    part = (len < 512 ? len : 512);
-                    inf.setInput(b, off, part);
-                    off += part;
-                    len -= part;
+                    inf.setInput(b, off, len);
+                    // Only use input buffer once.
+                    len = 0;
                 }
 
                 // Decompress and write blocks of output data
@@ -256,13 +249,14 @@ public class InflaterOutputStream extends FilterOutputStream {
                     }
                 } while (n > 0);
 
-                // Check the decompressor
-                if (inf.finished()) {
-                    break;
-                }
+                // Check for missing dictionary first
                 if (inf.needsDictionary()) {
                     throw new ZipException("ZLIB dictionary missing");
                 }
+                // Check the decompressor
+                if (inf.finished() || (len == 0) /* no more input */ ) {
+                    break;
+                }
             }
         } catch (DataFormatException ex) {
             // Improperly formatted compressed (ZIP) data
diff --git a/jdk/src/share/lib/security/java.policy b/jdk/src/share/lib/security/java.policy
index 8b6289720..baec2ea15 100644
--- a/jdk/src/share/lib/security/java.policy
+++ b/jdk/src/share/lib/security/java.policy
@@ -48,5 +48,7 @@ grant {
         permission java.util.PropertyPermission "java.vm.name", "read";
 
         permission java.util.PropertyPermission "sun.security.pkcs11.disableKeyExtraction", "read";
+
+        permission java.util.PropertyPermission "GZIP_USE_KAE", "read";
 };
 
diff --git a/jdk/src/share/native/java/util/zip/Deflater.c b/jdk/src/share/native/java/util/zip/Deflater.c
index c1bd34667..1b048e4f5 100644
--- a/jdk/src/share/native/java/util/zip/Deflater.c
+++ b/jdk/src/share/native/java/util/zip/Deflater.c
@@ -37,6 +37,7 @@
 #include "java_util_zip_Deflater.h"
 
 #define DEF_MEM_LEVEL 8
+#define KAE_DEFLATER_WindowBit 31
 
 static jfieldID levelID;
 static jfieldID strategyID;
@@ -104,6 +105,42 @@ Java_java_util_zip_Deflater_init(JNIEnv *env, jclass cls, jint level,
     }
 }
 
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Deflater_initKae(JNIEnv *env, jclass cls, jint level,
+                                 jint strategy)
+{
+    z_stream *strm = calloc(1, sizeof(z_stream));
+
+    if (strm == 0) {
+        JNU_ThrowOutOfMemoryError(env, 0);
+        return jlong_zero;
+    } else {
+        const char *msg;
+        int ret = deflateInit2(strm, level, Z_DEFLATED, KAE_DEFLATER_WindowBit, DEF_MEM_LEVEL, strategy);
+        switch (ret) {
+          case Z_OK:
+            return ptr_to_jlong(strm);
+          case Z_MEM_ERROR:
+            free(strm);
+            JNU_ThrowOutOfMemoryError(env, 0);
+            return jlong_zero;
+          case Z_STREAM_ERROR:
+            free(strm);
+            JNU_ThrowIllegalArgumentException(env, 0);
+            return jlong_zero;
+          default:
+            msg = ((strm->msg != NULL) ? strm->msg :
+                   (ret == Z_VERSION_ERROR) ?
+                   "zlib returned Z_VERSION_ERROR: "
+                   "compile time and runtime zlib implementations differ" :
+                   "unknown error initializing zlib library");
+            free(strm);
+            JNU_ThrowInternalError(env, msg);
+            return jlong_zero;
+        }
+    }
+}
+
 JNIEXPORT void JNICALL
 Java_java_util_zip_Deflater_setDictionary(JNIEnv *env, jclass cls, jlong addr,
                                           jarray b, jint off, jint len)
diff --git a/jdk/src/share/native/java/util/zip/Inflater.c b/jdk/src/share/native/java/util/zip/Inflater.c
index 79bcdd5b6..fca207215 100644
--- a/jdk/src/share/native/java/util/zip/Inflater.c
+++ b/jdk/src/share/native/java/util/zip/Inflater.c
@@ -41,6 +41,7 @@
 
 #define ThrowDataFormatException(env, msg) \
         JNU_ThrowByName(env, "java/util/zip/DataFormatException", msg)
+#define KAE_INFLATER_WindowBit 31
 
 static jfieldID needDictID;
 static jfieldID finishedID;
@@ -94,6 +95,39 @@ Java_java_util_zip_Inflater_init(JNIEnv *env, jclass cls, jboolean nowrap)
     }
 }
 
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Inflater_initKae(JNIEnv *env, jclass cls)
+{
+    z_stream *strm = calloc(1, sizeof(z_stream));
+
+    if (strm == NULL) {
+        JNU_ThrowOutOfMemoryError(env, 0);
+        return jlong_zero;
+    } else {
+        const char *msg;
+        int ret = inflateInit2(strm, KAE_INFLATER_WindowBit);
+        switch (ret) {
+          case Z_OK:
+            return ptr_to_jlong(strm);
+          case Z_MEM_ERROR:
+            free(strm);
+            JNU_ThrowOutOfMemoryError(env, 0);
+            return jlong_zero;
+          default:
+            msg = ((strm->msg != NULL) ? strm->msg :
+                   (ret == Z_VERSION_ERROR) ?
+                   "zlib returned Z_VERSION_ERROR: "
+                   "compile time and runtime zlib implementations differ" :
+                   (ret == Z_STREAM_ERROR) ?
+                   "inflateInit2 returned Z_STREAM_ERROR" :
+                   "unknown error initializing zlib library");
+            free(strm);
+            JNU_ThrowInternalError(env, msg);
+            return jlong_zero;
+        }
+    }
+}
+
 JNIEXPORT void JNICALL
 Java_java_util_zip_Inflater_setDictionary(JNIEnv *env, jclass cls, jlong addr,
                                           jarray b, jint off, jint len)
@@ -181,6 +215,70 @@ Java_java_util_zip_Inflater_inflateBytes(JNIEnv *env, jobject this, jlong addr,
     }
 }
 
+JNIEXPORT jint JNICALL
+Java_java_util_zip_Inflater_inflateBytesKAE(JNIEnv *env, jobject this, jlong addr,
+                                         jarray b, jint off, jint len)
+{
+    z_stream *strm = jlong_to_ptr(addr);
+    jarray this_buf = (jarray)(*env)->GetObjectField(env, this, bufID);
+    jint this_off = (*env)->GetIntField(env, this, offID);
+    jint this_len = (*env)->GetIntField(env, this, lenID);
+
+    jbyte *in_buf;
+    jbyte *out_buf;
+    int ret;
+
+    in_buf  = (*env)->GetPrimitiveArrayCritical(env, this_buf, 0);
+    if (in_buf == NULL) {
+        if (this_len != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0;
+    }
+    out_buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
+    if (out_buf == NULL) {
+        (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0);
+        if (len != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0;
+    }
+    strm->next_in  = (Bytef *) (in_buf + this_off);
+    strm->next_out = (Bytef *) (out_buf + off);
+    strm->avail_in  = this_len;
+    strm->avail_out = len;
+    ret = inflate(strm, Z_SYNC_FLUSH);
+    (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0);
+    (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0);
+
+    switch (ret) {
+    case Z_STREAM_END:
+        (*env)->SetBooleanField(env, this, finishedID, JNI_TRUE);
+        /* fall through */
+    case Z_OK:
+        this_off += this_len - strm->avail_in;
+        (*env)->SetIntField(env, this, offID, this_off);
+        (*env)->SetIntField(env, this, lenID, strm->avail_in);
+        return (jint) (len - strm->avail_out);
+    case Z_NEED_DICT:
+        (*env)->SetBooleanField(env, this, needDictID, JNI_TRUE);
+        /* Might have consumed some input here! */
+        this_off += this_len - strm->avail_in;
+        (*env)->SetIntField(env, this, offID, this_off);
+        (*env)->SetIntField(env, this, lenID, strm->avail_in);
+        return 0;
+    case Z_BUF_ERROR:
+        return 0;
+    case Z_DATA_ERROR:
+        ThrowDataFormatException(env, strm->msg);
+        return 0;
+    case Z_MEM_ERROR:
+        JNU_ThrowOutOfMemoryError(env, 0);
+        return 0;
+    default:
+        JNU_ThrowInternalError(env, strm->msg);
+        return 0;
+    }
+}
+
 JNIEXPORT jint JNICALL
 Java_java_util_zip_Inflater_getAdler(JNIEnv *env, jclass cls, jlong addr)
 {
diff --git a/jdk/test/java/util/zip/DeflateIn_InflateOut.java b/jdk/test/java/util/zip/DeflateIn_InflateOut.java
index dd69a7773..048d8e34c 100644
--- a/jdk/test/java/util/zip/DeflateIn_InflateOut.java
+++ b/jdk/test/java/util/zip/DeflateIn_InflateOut.java
@@ -41,14 +41,31 @@ public class DeflateIn_InflateOut {
     private static ByteArrayOutputStream baos;
     private static InflaterOutputStream ios;
 
-    private static void reset() {
+    private static Inflater reset(byte[] dict) {
         new Random(new Date().getTime()).nextBytes(data);
 
         bais = new ByteArrayInputStream(data);
-        dis = new DeflaterInputStream(bais);
+        if (dict == null) {
+            dis = new DeflaterInputStream(bais);
+        } else {
+            Deflater def = new Deflater();
+            def.setDictionary(dict);
+            dis = new DeflaterInputStream(bais, def);
+        }
 
         baos = new ByteArrayOutputStream();
-        ios = new InflaterOutputStream(baos);
+        if (dict == null) {
+            ios = new InflaterOutputStream(baos);
+            return null;
+        } else {
+            Inflater inf = new Inflater();
+            ios = new InflaterOutputStream(baos, inf);
+            return inf;
+        }
+    }
+
+    private static void reset() {
+        reset(null);
     }
 
     /** Check byte arrays read/write. */
@@ -216,6 +233,44 @@ public class DeflateIn_InflateOut {
         check(numNotSkipped + numSkipBytes == numReadable);
     }
 
+    /** Check "needsDictionary()". */
+    private static void NeedsDictionary() throws Throwable {
+        byte[] dict = {1, 2, 3, 4};
+        Adler32 adler32 = new Adler32();
+        adler32.update(dict);
+        long checksum = adler32.getValue();
+        byte[] buf = new byte[512];
+
+        Inflater inf = reset(dict);
+        check(dis.available() == 1);
+        boolean dictSet = false;
+        for (;;) {
+            int len = dis.read(buf, 0, buf.length);
+            if (len < 0) {
+                break;
+            } else {
+                try {
+                    ios.write(buf, 0, len);
+                    if (dictSet == false) {
+                        check(false, "Must throw ZipException without dictionary");
+                        return;
+                    }
+                } catch (ZipException ze) {
+                    check(dictSet == false, "Dictonary must be set only once");
+                    check(checksum == inf.getAdler(), "Incorrect dictionary");
+                    inf.setDictionary(dict);
+                    // After setting the dictionary, we have to flush the
+                    // InflaterOutputStream now in order to consume all the
+                    // pending input data from the last, failed call to "write()".
+                    ios.flush();
+                    dictSet = true;
+                }
+            }
+        }
+        check(dis.available() == 0);
+        ios.close();
+        check(Arrays.equals(data, baos.toByteArray()));
+    }
 
     public static void realMain(String[] args) throws Throwable {
         ArrayReadWrite();
@@ -227,15 +282,24 @@ public class DeflateIn_InflateOut {
         ByteReadByteWrite();
 
         SkipBytes();
+
+        NeedsDictionary();
     }
 
     //--------------------- Infrastructure ---------------------------
     static volatile int passed = 0, failed = 0;
     static void pass() {passed++;}
-    static void fail() {failed++; Thread.dumpStack();}
-    static void fail(String msg) {System.out.println(msg); fail();}
+    static void fail() { fail(null); }
+    static void fail(String msg) {
+        failed++;
+        if (msg != null) {
+            System.err.println(msg);
+        }
+        Thread.dumpStack();
+    }
     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
     static void check(boolean cond) {if (cond) pass(); else fail();}
+    static void check(boolean cond, String msg) {if (cond) pass(); else fail(msg);}
     static void equal(Object x, Object y) {
         if (x == null ? y == null : x.equals(y)) pass();
         else fail(x + " not equal to " + y);}
-- 
2.23.0

