It would be convenient to be able to inflate/deflate a direct or heap
byte buffer without having to copy it through an array first.  For my
Friday mini-project this week, I've decided to take a crack at this.
The attached patch is the result.  Would anyone be interested in
reviewing and maybe sponsoring this change?  It would be really great
to get it in to JDK 11.

The patch includes a modification to FlaterTest to run through
permutations of using direct and heap byte buffers.  Though I couldn't
get jtreg working, I did compile and run the test by hand and it seems
to pass; the build also works fine with the new code.

Extra thanks to Martin Balao for pointing me towards mapfile-vers when
I couldn't figure out why I was getting UnsatisfiedLinkError.  That
one was not obvious.
-- 
- DML
commit becd36e852e55a29a4685577453944552c817b66
Author: David M. Lloyd <david.ll...@redhat.com>
Date:   Fri Feb 16 11:00:10 2018 -0600

    [JDK-6341887] Update Inflater/Deflator to handle ByteBuffer

diff --git a/make/mapfiles/libzip/mapfile-vers 
b/make/mapfiles/libzip/mapfile-vers
index d711d8e17f4..11ccc2d6ecb 100644
--- a/make/mapfiles/libzip/mapfile-vers
+++ b/make/mapfiles/libzip/mapfile-vers
@@ -33,20 +33,28 @@ SUNWprivate_1.1 {
                Java_java_util_zip_CRC32_update;
                Java_java_util_zip_CRC32_updateBytes0;
                Java_java_util_zip_CRC32_updateByteBuffer0;
-               Java_java_util_zip_Deflater_deflateBytes;
+               Java_java_util_zip_Deflater_deflateBytesBytes;
+               Java_java_util_zip_Deflater_deflateBytesBuffer;
+               Java_java_util_zip_Deflater_deflateBufferBytes;
+               Java_java_util_zip_Deflater_deflateBufferBuffer;
                Java_java_util_zip_Deflater_end;
                Java_java_util_zip_Deflater_getAdler;
                Java_java_util_zip_Deflater_init;
                Java_java_util_zip_Deflater_initIDs;
                Java_java_util_zip_Deflater_reset;
                Java_java_util_zip_Deflater_setDictionary;
+               Java_java_util_zip_Deflater_setDictionaryBuffer;
                Java_java_util_zip_Inflater_end;
                Java_java_util_zip_Inflater_getAdler;
-               Java_java_util_zip_Inflater_inflateBytes;
+               Java_java_util_zip_Inflater_inflateBytesBytes;
+               Java_java_util_zip_Inflater_inflateBytesBuffer;
+               Java_java_util_zip_Inflater_inflateBufferBytes;
+               Java_java_util_zip_Inflater_inflateBufferBuffer;
                Java_java_util_zip_Inflater_init;
                Java_java_util_zip_Inflater_initIDs;
                Java_java_util_zip_Inflater_reset;
                Java_java_util_zip_Inflater_setDictionary;
+               Java_java_util_zip_Inflater_setDictionaryBuffer;
                ZIP_Close;
                ZIP_CRC32;
                ZIP_FreeEntry;
diff --git a/src/java.base/share/classes/java/util/zip/Deflater.java 
b/src/java.base/share/classes/java/util/zip/Deflater.java
index c75dd4a33f0..5ededeb56ca 100644
--- a/src/java.base/share/classes/java/util/zip/Deflater.java
+++ b/src/java.base/share/classes/java/util/zip/Deflater.java
@@ -26,6 +26,9 @@
 package java.util.zip;
 
 import java.lang.ref.Cleaner.Cleanable;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
 import jdk.internal.ref.CleanerFactory;
 
 /**
@@ -92,8 +95,7 @@ import jdk.internal.ref.CleanerFactory;
 public class Deflater {
 
     private final DeflaterZStreamRef zsRef;
-    private byte[] buf = new byte[0];
-    private int off, len;
+    private ByteBuffer input;
     private int level, strategy;
     private boolean setParams;
     private boolean finish, finished;
@@ -216,17 +218,11 @@ public class Deflater {
      * @see Deflater#needsInput
      */
     public void setInput(byte[] b, int off, int len) {
-        if (b== null) {
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(b);
         if (off < 0 || len < 0 || off > b.length - len) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        synchronized (zsRef) {
-            this.buf = b;
-            this.off = off;
-            this.len = len;
-        }
+        setInput(ByteBuffer.wrap(b, off, len));
     }
 
     /**
@@ -236,7 +232,22 @@ public class Deflater {
      * @see Deflater#needsInput
      */
     public void setInput(byte[] b) {
-        setInput(b, 0, b.length);
+        // freeload the NPE off of wrap()
+        setInput(ByteBuffer.wrap(b));
+    }
+
+    /**
+     * Sets input data for compression. This should be called whenever
+     * needsInput() returns true indicating that more input data is required.
+     * @param byteBuffer the input data bytes
+     * @see Deflater#needsInput
+     * @since 11
+     */
+    public void setInput(ByteBuffer byteBuffer) {
+        Objects.requireNonNull(byteBuffer);
+        synchronized (zsRef) {
+            this.input = byteBuffer;
+        }
     }
 
     /**
@@ -252,16 +263,11 @@ public class Deflater {
      * @see Inflater#getAdler
      */
     public void setDictionary(byte[] b, int off, int len) {
-        if (b == null) {
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(b);
         if (off < 0 || len < 0 || off > b.length - len) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        synchronized (zsRef) {
-            ensureOpen();
-            setDictionary(zsRef.address(), b, off, len);
-        }
+        setDictionary(ByteBuffer.wrap(b, off, len));
     }
 
     /**
@@ -275,9 +281,36 @@ public class Deflater {
      * @see Inflater#getAdler
      */
     public void setDictionary(byte[] b) {
-        setDictionary(b, 0, b.length);
+        Objects.requireNonNull(b);
+        setDictionary(ByteBuffer.wrap(b));
     }
 
+    /**
+     * Sets preset dictionary for compression. A preset dictionary is used
+     * when the history buffer can be predetermined. When the data is later
+     * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
+     * in order to get the Adler-32 value of the dictionary required for
+     * decompression.
+     * @param byteBuffer the dictionary data bytes
+     * @see Inflater#inflate
+     * @see Inflater#getAdler
+     */
+    public void setDictionary(ByteBuffer byteBuffer) {
+        Objects.requireNonNull(byteBuffer);
+        synchronized (zsRef) {
+            ensureOpen();
+            final int limit = byteBuffer.limit();
+            final int position = byteBuffer.position();
+            if (byteBuffer.isDirect()) {
+                setDictionaryBuffer(zsRef.address(), byteBuffer, position, 
Math.max(limit - position, 0));
+            } else {
+                final byte[] array = ZipUtils.getBufferArray(byteBuffer);
+                final int offset = ZipUtils.getBufferOffset(byteBuffer);
+                setDictionary(zsRef.address(), array, offset + position, 
Math.max(limit - position, 0));
+            }
+            byteBuffer.position(limit);
+        }
+    }
     /**
      * Sets the compression strategy to the specified value.
      *
@@ -338,7 +371,7 @@ public class Deflater {
      */
     public boolean needsInput() {
         synchronized (zsRef) {
-            return len <= 0;
+            return ! input.hasRemaining();
         }
     }
 
@@ -404,6 +437,26 @@ public class Deflater {
         return deflate(b, 0, b.length, NO_FLUSH);
     }
 
+    /**
+     * Compresses the input data and fills specified buffer with compressed
+     * data. Returns actual number of bytes of compressed data. A return value
+     * of 0 indicates that {@link #needsInput() needsInput} should be called
+     * in order to determine if more input data is required.
+     *
+     * <p>This method uses {@link #NO_FLUSH} as its compression flush mode.
+     * An invocation of this method of the form {@code deflater.deflate(b)}
+     * yields the same result as the invocation of
+     * {@code deflater.deflate(b, 0, b.length, Deflater.NO_FLUSH)}.
+     *
+     * @param output the buffer for the compressed data
+     * @return the actual number of bytes of compressed data written to the
+     *         output buffer
+     * @since 11
+     */
+    public int deflate(ByteBuffer output) {
+        return deflate(output, NO_FLUSH);
+    }
+
     /**
      * Compresses the input data and fills the specified buffer with compressed
      * data. Returns actual number of bytes of data compressed.
@@ -452,21 +505,109 @@ public class Deflater {
      * @since 1.7
      */
     public int deflate(byte[] b, int off, int len, int flush) {
-        if (b == null) {
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(b);
         if (off < 0 || len < 0 || off > b.length - len) {
             throw new ArrayIndexOutOfBoundsException();
         }
+        return deflate(ByteBuffer.wrap(b, off, len), flush);
+    }
+
+    /**
+     * Compresses the input data and fills the specified buffer with compressed
+     * data. Returns actual number of bytes of data compressed.
+     *
+     * <p>Compression flush mode is one of the following three modes:
+     *
+     * <ul>
+     * <li>{@link #NO_FLUSH}: allows the deflater to decide how much data
+     * to accumulate, before producing output, in order to achieve the best
+     * compression (should be used in normal use scenario). A return value
+     * of 0 in this flush mode indicates that {@link #needsInput()} should
+     * be called in order to determine if more input data is required.
+     *
+     * <li>{@link #SYNC_FLUSH}: all pending output in the deflater is flushed,
+     * to the specified output buffer, so that an inflater that works on
+     * compressed data can get all input data available so far (In particular
+     * the {@link #needsInput()} returns {@code true} after this invocation
+     * if enough output space is provided). Flushing with {@link #SYNC_FLUSH}
+     * may degrade compression for some compression algorithms and so it
+     * should be used only when necessary.
+     *
+     * <li>{@link #FULL_FLUSH}: all pending output is flushed out as with
+     * {@link #SYNC_FLUSH}. The compression state is reset so that the inflater
+     * that works on the compressed output data can restart from this point
+     * if previous compressed data has been damaged or if random access is
+     * desired. Using {@link #FULL_FLUSH} too often can seriously degrade
+     * compression.
+     * </ul>
+     *
+     * <p>In the case of {@link #FULL_FLUSH} or {@link #SYNC_FLUSH}, if
+     * the return value is {@code len}, the space available in output
+     * buffer {@code b}, this method should be invoked again with the same
+     * {@code flush} parameter and more output space. Make sure that
+     * {@code len} is greater than 6 to avoid flush marker (5 bytes) being
+     * repeatedly output to the output buffer every time this method is
+     * invoked.
+     *
+     * @param output the buffer for the compressed data
+     * @param flush the compression flush mode
+     * @return the actual number of bytes of compressed data written to
+     *         the output buffer
+     *
+     * @throws IllegalArgumentException if the flush mode is invalid
+     * @since 11
+     */
+    public int deflate(ByteBuffer output, int flush) {
+        Objects.requireNonNull(output);
         synchronized (zsRef) {
             ensureOpen();
             if (flush == NO_FLUSH || flush == SYNC_FLUSH ||
                 flush == FULL_FLUSH) {
-                int thisLen = this.len;
-                int n = deflateBytes(zsRef.address(), b, off, len, flush);
-                bytesWritten += n;
-                bytesRead += (thisLen - this.len);
-                return n;
+
+                final ByteBuffer input = this.input;
+                long result;
+                final int inputPos = input.position();
+                final int outputPos = output.position();
+                final int inputRem = Math.max(input.limit() - inputPos, 0);
+                final int outputRem = Math.max(output.limit() - outputPos, 0);
+                if (input.isDirect()) {
+                    if (output.isDirect()) {
+                        result = deflateBufferBuffer(zsRef.address(),
+                            input, inputPos, inputRem,
+                            output, outputPos, outputRem,
+                            flush);
+                    } else {
+                        final byte[] outputArray = 
ZipUtils.getBufferArray(output);
+                        final int outputOffset = 
ZipUtils.getBufferOffset(output);
+                        result = deflateBufferBytes(zsRef.address(),
+                            input, inputPos, inputRem,
+                            outputArray, outputOffset + outputPos, outputRem,
+                            flush);
+                    }
+                } else {
+                    final byte[] inputArray = ZipUtils.getBufferArray(input);
+                    final int inputOffset = ZipUtils.getBufferOffset(input);
+                    if (output.isDirect()) {
+                        result = deflateBytesBuffer(zsRef.address(),
+                            inputArray, inputOffset + inputPos, inputRem,
+                            output, outputPos, outputRem,
+                            flush);
+                    } else {
+                        final byte[] outputArray = 
ZipUtils.getBufferArray(output);
+                        final int outputOffset = 
ZipUtils.getBufferOffset(output);
+                        result = deflateBytesBytes(zsRef.address(),
+                            inputArray, inputOffset + inputPos, inputRem,
+                            outputArray, outputOffset + outputPos, outputRem,
+                            flush);
+                    }
+                }
+                int read = (int) (result & 0x7fff_ffffL);
+                int written = (int) (result >>> 31);
+                input.position(inputPos + read);
+                output.position(outputPos + written);
+                bytesWritten += written;
+                bytesRead += read;
+                return written;
             }
             throw new IllegalArgumentException();
         }
@@ -545,7 +686,7 @@ public class Deflater {
             reset(zsRef.address());
             finish = false;
             finished = false;
-            off = len = 0;
+            input = ZipUtils.defaultBuf;
             bytesRead = bytesWritten = 0;
         }
     }
@@ -560,7 +701,7 @@ public class Deflater {
     public void end() {
         synchronized (zsRef) {
             zsRef.clean();
-            buf = null;
+            input = ZipUtils.defaultBuf;
         }
     }
 
@@ -587,9 +728,22 @@ public class Deflater {
 
     private static native void initIDs();
     private static native long init(int level, int strategy, boolean nowrap);
-    private static native void setDictionary(long addr, byte[] b, int off, int 
len);
-    private native int deflateBytes(long addr, byte[] b, int off, int len,
-                                    int flush);
+    private static native void setDictionary(long addr, byte[] b, int off,
+                                             int len);
+    private static native void setDictionaryBuffer(long addr, ByteBuffer 
buffer, int off,
+                                             int len);
+    private native long deflateBytesBytes(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen, int flush);
+    private native long deflateBytesBuffer(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        ByteBuffer outputBuffer, int outputPos, int outputLen, int flush);
+    private native long deflateBufferBytes(long addr,
+        ByteBuffer inputBuffer, int inputPos, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen, int flush);
+    private native long deflateBufferBuffer(long addr,
+        ByteBuffer inputBuffer, int inputPos, int inputLen,
+        ByteBuffer outputBuffer, int outputPos, int outputLen, int flush);
     private static native int getAdler(long addr);
     private static native void reset(long addr);
     private static native void end(long addr);
diff --git a/src/java.base/share/classes/java/util/zip/Inflater.java 
b/src/java.base/share/classes/java/util/zip/Inflater.java
index 9c6d8aa3d83..43455602c01 100644
--- a/src/java.base/share/classes/java/util/zip/Inflater.java
+++ b/src/java.base/share/classes/java/util/zip/Inflater.java
@@ -26,6 +26,10 @@
 package java.util.zip;
 
 import java.lang.ref.Cleaner.Cleanable;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.util.Objects;
+
 import jdk.internal.ref.CleanerFactory;
 
 /**
@@ -92,15 +96,12 @@ import jdk.internal.ref.CleanerFactory;
 public class Inflater {
 
     private final InflaterZStreamRef zsRef;
-    private byte[] buf = defaultBuf;
-    private int off, len;
+    private ByteBuffer input = ZipUtils.defaultBuf;
     private boolean finished;
     private boolean needDict;
     private long bytesRead;
     private long bytesWritten;
 
-    private static final byte[] defaultBuf = new byte[0];
-
     static {
         ZipUtils.loadLibrary();
         initIDs();
@@ -138,17 +139,11 @@ public class Inflater {
      * @see Inflater#needsInput
      */
     public void setInput(byte[] b, int off, int len) {
-        if (b == null) {
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(b);
         if (off < 0 || len < 0 || off > b.length - len) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        synchronized (zsRef) {
-            this.buf = b;
-            this.off = off;
-            this.len = len;
-        }
+        setInput(ByteBuffer.wrap(b, off, len));
     }
 
     /**
@@ -159,7 +154,23 @@ public class Inflater {
      * @see Inflater#needsInput
      */
     public void setInput(byte[] b) {
-        setInput(b, 0, b.length);
+        // freeload the NPE off of wrap()
+        setInput(ByteBuffer.wrap(b));
+    }
+
+    /**
+     * Sets input data for decompression. Should be called whenever
+     * needsInput() returns true indicating that more input data is
+     * required.
+     * @param byteBuffer the input data bytes
+     * @see Inflater#needsInput
+     * @since 11
+     */
+    public void setInput(ByteBuffer byteBuffer) {
+        Objects.requireNonNull(byteBuffer);
+        synchronized (zsRef) {
+            this.input = byteBuffer;
+        }
     }
 
     /**
@@ -174,17 +185,11 @@ public class Inflater {
      * @see Inflater#getAdler
      */
     public void setDictionary(byte[] b, int off, int len) {
-        if (b == null) {
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(b);
         if (off < 0 || len < 0 || off > b.length - len) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        synchronized (zsRef) {
-            ensureOpen();
-            setDictionary(zsRef.address(), b, off, len);
-            needDict = false;
-        }
+        setDictionary(ByteBuffer.wrap(b, off, len));
     }
 
     /**
@@ -197,7 +202,36 @@ public class Inflater {
      * @see Inflater#getAdler
      */
     public void setDictionary(byte[] b) {
-        setDictionary(b, 0, b.length);
+        Objects.requireNonNull(b);
+        setDictionary(ByteBuffer.wrap(b));
+    }
+
+    /**
+     * Sets the preset dictionary to the given array of bytes. Should be
+     * called when inflate() returns 0 and needsDictionary() returns true
+     * indicating that a preset dictionary is required. The method getAdler()
+     * can be used to get the Adler-32 value of the dictionary needed.
+     * @param byteBuffer the dictionary data bytes
+     * @see Inflater#needsDictionary
+     * @see Inflater#getAdler
+     * @since 11
+     */
+    public void setDictionary(ByteBuffer byteBuffer) {
+        Objects.requireNonNull(byteBuffer);
+        synchronized (zsRef) {
+            ensureOpen();
+            final int limit = byteBuffer.limit();
+            final int position = byteBuffer.position();
+            if (byteBuffer.isDirect()) {
+                setDictionaryBuffer(zsRef.address(), byteBuffer, position, 
Math.max(limit - position, 0));
+            } else {
+                final byte[] array = ZipUtils.getBufferArray(byteBuffer);
+                final int offset = ZipUtils.getBufferOffset(byteBuffer);
+                setDictionary(zsRef.address(), array, offset + position, 
Math.max(limit - position, 0));
+            }
+            byteBuffer.position(limit);
+            needDict = false;
+        }
     }
 
     /**
@@ -208,7 +242,7 @@ public class Inflater {
      */
     public int getRemaining() {
         synchronized (zsRef) {
-            return len;
+            return input.remaining();
         }
     }
 
@@ -220,7 +254,7 @@ public class Inflater {
      */
     public boolean needsInput() {
         synchronized (zsRef) {
-            return len <= 0;
+            return ! input.hasRemaining();
         }
     }
 
@@ -265,20 +299,11 @@ public class Inflater {
     public int inflate(byte[] b, int off, int len)
         throws DataFormatException
     {
-        if (b == null) {
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(b);
         if (off < 0 || len < 0 || off > b.length - len) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        synchronized (zsRef) {
-            ensureOpen();
-            int thisLen = this.len;
-            int n = inflateBytes(zsRef.address(), b, off, len);
-            bytesWritten += n;
-            bytesRead += (thisLen - this.len);
-            return n;
-        }
+        return inflate(ByteBuffer.wrap(b, off, len));
     }
 
     /**
@@ -295,7 +320,73 @@ public class Inflater {
      * @see Inflater#needsDictionary
      */
     public int inflate(byte[] b) throws DataFormatException {
-        return inflate(b, 0, b.length);
+        Objects.requireNonNull(b);
+        return inflate(ByteBuffer.wrap(b));
+    }
+
+    /**
+     * Uncompresses bytes into specified buffer. Returns actual number
+     * of bytes uncompressed. A return value of 0 indicates that
+     * needsInput() or needsDictionary() should be called in order to
+     * determine if more input data or a preset dictionary is required.
+     * In the latter case, getAdler() can be used to get the Adler-32
+     * value of the dictionary required.
+     * @param output the buffer for the uncompressed data
+     * @return the actual number of uncompressed bytes
+     * @throws DataFormatException if the compressed data format is invalid
+     * @throws ReadOnlyBufferException if the given output buffer is read-only
+     * @see Inflater#needsInput
+     * @see Inflater#needsDictionary
+     * @since 11
+     */
+    public int inflate(ByteBuffer output) throws DataFormatException {
+        Objects.requireNonNull(output);
+        if (output.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        synchronized (zsRef) {
+            ensureOpen();
+            final ByteBuffer input = this.input;
+            long result;
+            final int inputPos = input.position();
+            final int outputPos = output.position();
+            final int inputRem = Math.max(input.limit() - inputPos, 0);
+            final int outputRem = Math.max(output.limit() - outputPos, 0);
+            if (input.isDirect()) {
+                if (output.isDirect()) {
+                    result = inflateBufferBuffer(zsRef.address(),
+                        input, inputPos, inputRem,
+                        output, outputPos, outputRem);
+                } else {
+                    final byte[] outputArray = ZipUtils.getBufferArray(output);
+                    final int outputOffset = ZipUtils.getBufferOffset(output);
+                    result = inflateBufferBytes(zsRef.address(),
+                        input, inputPos, inputRem,
+                        outputArray, outputOffset + outputPos, outputRem);
+                }
+            } else {
+                final byte[] inputArray = ZipUtils.getBufferArray(input);
+                final int inputOffset = ZipUtils.getBufferOffset(input);
+                if (output.isDirect()) {
+                    result = inflateBytesBuffer(zsRef.address(),
+                        inputArray, inputOffset + inputPos, inputRem,
+                        output, outputPos, outputRem);
+                } else {
+                    final byte[] outputArray = ZipUtils.getBufferArray(output);
+                    final int outputOffset = ZipUtils.getBufferOffset(output);
+                    result = inflateBytesBytes(zsRef.address(),
+                        inputArray, inputOffset + inputPos, inputRem,
+                        outputArray, outputOffset + outputPos, outputRem);
+                }
+            }
+            int read = (int) (result & 0x7fff_ffffL);
+            int written = (int) (result >>> 31);
+            input.position(inputPos + read);
+            output.position(outputPos + written);
+            bytesWritten += written;
+            bytesRead += read;
+            return written;
+        }
     }
 
     /**
@@ -368,10 +459,9 @@ public class Inflater {
         synchronized (zsRef) {
             ensureOpen();
             reset(zsRef.address());
-            buf = defaultBuf;
+            input = ZipUtils.defaultBuf;
             finished = false;
             needDict = false;
-            off = len = 0;
             bytesRead = bytesWritten = 0;
         }
     }
@@ -386,7 +476,7 @@ public class Inflater {
     public void end() {
         synchronized (zsRef) {
             zsRef.clean();
-            buf = null;
+            input = null;
         }
     }
 
@@ -426,8 +516,20 @@ public class Inflater {
     private static native long init(boolean nowrap);
     private static native 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 static native void setDictionaryBuffer(long addr, ByteBuffer 
buffer, int off,
+                                             int len);
+    private native long inflateBytesBytes(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen) throws 
DataFormatException;
+    private native long inflateBytesBuffer(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        ByteBuffer outputBuffer, int outputPos, int outputLen) throws 
DataFormatException;
+    private native long inflateBufferBytes(long addr,
+        ByteBuffer inputBuffer, int inputPos, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen) throws 
DataFormatException;
+    private native long inflateBufferBuffer(long addr,
+        ByteBuffer inputBuffer, int inputPos, int inputLen,
+        ByteBuffer outputBuffer, int outputPos, int outputLen) throws 
DataFormatException;
     private static native int getAdler(long addr);
     private static native void reset(long addr);
     private static native void end(long addr);
diff --git a/src/java.base/share/classes/java/util/zip/ZipUtils.java 
b/src/java.base/share/classes/java/util/zip/ZipUtils.java
index 45c5d8dbb67..c2a8fb238ce 100644
--- a/src/java.base/share/classes/java/util/zip/ZipUtils.java
+++ b/src/java.base/share/classes/java/util/zip/ZipUtils.java
@@ -25,6 +25,7 @@
 
 package java.util.zip;
 
+import java.nio.ByteBuffer;
 import java.nio.file.attribute.FileTime;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -37,6 +38,8 @@ import java.util.concurrent.TimeUnit;
 
 import static java.util.zip.ZipConstants.ENDHDR;
 
+import jdk.internal.misc.Unsafe;
+
 class ZipUtils {
 
     // used to adjust values between Windows and java epoch
@@ -45,6 +48,8 @@ class ZipUtils {
     // used to indicate the corresponding windows time is not available
     public static final long WINDOWS_TIME_NOT_AVAILABLE = Long.MIN_VALUE;
 
+    static final ByteBuffer defaultBuf = ByteBuffer.allocateDirect(0);
+
     /**
      * Converts Windows time (in microseconds, UTC/GMT) time to FileTime.
      */
@@ -281,4 +286,17 @@ class ZipUtils {
             AccessController.doPrivileged(pa);
         }
     }
+
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    private static final long byteBufferArrayOffset = 
unsafe.objectFieldOffset(ByteBuffer.class, "hb");
+    private static final long byteBufferOffsetOffset = 
unsafe.objectFieldOffset(ByteBuffer.class, "offset");
+
+    static byte[] getBufferArray(ByteBuffer byteBuffer) {
+        return (byte[]) unsafe.getObject(byteBuffer, byteBufferArrayOffset);
+    }
+
+    static int getBufferOffset(ByteBuffer byteBuffer) {
+        return unsafe.getInt(byteBuffer, byteBufferOffsetOffset);
+    }
 }
diff --git a/src/java.base/share/native/libzip/Deflater.c 
b/src/java.base/share/native/libzip/Deflater.c
index b666a16145a..d6d78a1b586 100644
--- a/src/java.base/share/native/libzip/Deflater.c
+++ b/src/java.base/share/native/libzip/Deflater.c
@@ -58,12 +58,6 @@ Java_java_util_zip_Deflater_initIDs(JNIEnv *env, jclass cls)
     CHECK_NULL(finishID);
     finishedID = (*env)->GetFieldID(env, cls, "finished", "Z");
     CHECK_NULL(finishedID);
-    bufID = (*env)->GetFieldID(env, cls, "buf", "[B");
-    CHECK_NULL(bufID);
-    offID = (*env)->GetFieldID(env, cls, "off", "I");
-    CHECK_NULL(offID);
-    lenID = (*env)->GetFieldID(env, cls, "len", "I");
-    CHECK_NULL(lenID);
 }
 
 JNIEXPORT jlong JNICALL
@@ -104,22 +98,16 @@ Java_java_util_zip_Deflater_init(JNIEnv *env, jclass cls, 
jint level,
     }
 }
 
-JNIEXPORT void JNICALL
-Java_java_util_zip_Deflater_setDictionary(JNIEnv *env, jclass cls, jlong addr,
-                                          jarray b, jint off, jint len)
+static void doSetDictionary(JNIEnv *env, jlong addr, jbyte *buf, jint off,
+                            jint len)
 {
-    Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
-    int res;
-    if (buf == 0) {/* out of memory */
-        return;
-    }
-    res = deflateSetDictionary((z_stream *)jlong_to_ptr(addr), buf + off, len);
-    (*env)->ReleasePrimitiveArrayCritical(env, b, buf, 0);
+    int res = deflateSetDictionary(jlong_to_ptr(addr), (Bytef *) (buf + off), 
len);
     switch (res) {
     case Z_OK:
         break;
     case Z_STREAM_ERROR:
-        JNU_ThrowIllegalArgumentException(env, 0);
+    case Z_DATA_ERROR:
+        JNU_ThrowIllegalArgumentException(env, ((z_stream 
*)jlong_to_ptr(addr))->msg);
         break;
     default:
         JNU_ThrowInternalError(env, ((z_stream *)jlong_to_ptr(addr))->msg);
@@ -127,94 +115,188 @@ Java_java_util_zip_Deflater_setDictionary(JNIEnv *env, 
jclass cls, jlong addr,
     }
 }
 
-JNIEXPORT jint JNICALL
-Java_java_util_zip_Deflater_deflateBytes(JNIEnv *env, jobject this, jlong addr,
-                                         jarray b, jint off, jint len, jint 
flush)
+JNIEXPORT void JNICALL
+Java_java_util_zip_Deflater_setDictionary(JNIEnv *env, jclass cls, jlong addr,
+                                          jbyteArray b, jint off, jint len)
+{
+    jbyte *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
+    if (buf == NULL) /* out of memory */
+        return;
+    doSetDictionary(env, addr, buf, off, len);
+    (*env)->ReleasePrimitiveArrayCritical(env, b, buf, 0);
+}
+
+JNIEXPORT void JNICALL
+Java_java_util_zip_Deflater_setDictionaryBuffer(JNIEnv *env, jclass cls, jlong 
addr,
+                                          jobject buffer, jint off, jint len)
+{
+    jbyte *buf = (*env)->GetDirectBufferAddress(env, buffer);
+    if (buf == NULL) { /* ??? */
+        JNU_ThrowInternalError(env, "should not reach here");
+        return;
+    }
+    doSetDictionary(env, addr, buf, off, len);
+}
+
+static jlong doDeflate(JNIEnv *env, jobject this, jlong addr,
+                       jbyte *input, jint inputOff, jint inputLen,
+                       jbyte *output, jint outputOff, jint outputLen,
+                       jint flush)
 {
     z_stream *strm = jlong_to_ptr(addr);
+    jint inputUsed = 0, outputUsed = 0;
+
+    strm->next_in  = (Bytef *) (input + inputOff);
+    strm->next_out = (Bytef *) (output + outputOff);
+    strm->avail_in  = inputLen;
+    strm->avail_out = outputLen;
 
-    jarray this_buf = (*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 res;
     if ((*env)->GetBooleanField(env, this, setParamsID)) {
         int level = (*env)->GetIntField(env, this, levelID);
         int strategy = (*env)->GetIntField(env, this, strategyID);
-        in_buf = (*env)->GetPrimitiveArrayCritical(env, this_buf, 0);
-        if (in_buf == NULL) {
-            // Throw OOME only when length is not zero
-            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;
-        res = deflateParams(strm, level, strategy);
-        (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0);
-        (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0);
+        int res = deflateParams(strm, level, strategy);
         switch (res) {
         case Z_OK:
             (*env)->SetBooleanField(env, this, setParamsID, JNI_FALSE);
+            // fall through
         case Z_BUF_ERROR:
-            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);
+            inputUsed = inputLen - strm->avail_in;
+            outputUsed = outputLen - strm->avail_out;
+            break;
         default:
             JNU_ThrowInternalError(env, strm->msg);
             return 0;
         }
     } else {
         jboolean finish = (*env)->GetBooleanField(env, this, finishID);
-        in_buf = (*env)->GetPrimitiveArrayCritical(env, this_buf, 0);
-        if (in_buf == NULL) {
-            if (this_len != 0)
-                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)
-                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;
-        res = deflate(strm, finish ? Z_FINISH : flush);
-        (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0);
-        (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0);
+        int res = deflate(strm, finish ? Z_FINISH : flush);
         switch (res) {
         case Z_STREAM_END:
             (*env)->SetBooleanField(env, this, finishedID, JNI_TRUE);
             /* fall through */
         case Z_OK:
         case Z_BUF_ERROR:
-            this_off += this_len - strm->avail_in;
-            (*env)->SetIntField(env, this, offID, this_off);
-            (*env)->SetIntField(env, this, lenID, strm->avail_in);
-            return len - strm->avail_out;
+            inputUsed = inputLen - strm->avail_in;
+            outputUsed = outputLen - strm->avail_out;
+            break;
         default:
             JNU_ThrowInternalError(env, strm->msg);
             return 0;
         }
     }
+    return ((jlong)inputUsed) | (((jlong)outputUsed) << 31);
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Deflater_deflateBytesBytes(JNIEnv *env, jobject this, jlong 
addr,
+                                         jbyteArray inputArray, jint inputOff, 
jint inputLen,
+                                         jbyteArray outputArray, jint 
outputOff, jint outputLen,
+                                         jint flush)
+{
+    jbyte *input = (*env)->GetPrimitiveArrayCritical(env, inputArray, 0);
+    if (input == NULL) {
+        if (inputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
+    }
+    jbyte *output = (*env)->GetPrimitiveArrayCritical(env, outputArray, 0);
+    if (output == NULL) {
+        (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+        if (outputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
+    }
+
+    jlong retVal = doDeflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen,
+            flush);
+
+    (*env)->ReleasePrimitiveArrayCritical(env, outputArray, output, 0);
+    (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+
+    return retVal;
+}
+
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Deflater_deflateBytesBuffer(JNIEnv *env, jobject this, 
jlong addr,
+                                         jbyteArray inputArray, jint inputOff, 
jint inputLen,
+                                         jobject outputBuffer, jint outputOff, 
jint outputLen,
+                                         jint flush)
+{
+    jbyte *input = (*env)->GetPrimitiveArrayCritical(env, inputArray, 0);
+    if (input == NULL) {
+        if (inputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
+    }
+    jbyte *output = (*env)->GetDirectBufferAddress(env, outputBuffer);
+    if (output == NULL) {
+        (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+
+    jlong retVal = doDeflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen,
+            flush);
+
+    (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+
+    return retVal;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Deflater_deflateBufferBytes(JNIEnv *env, jobject this, 
jlong addr,
+                                         jobject inputBuffer, jint inputOff, 
jint inputLen,
+                                         jbyteArray outputArray, jint 
outputOff, jint outputLen,
+                                         jint flush)
+{
+    jbyte *input = (*env)->GetDirectBufferAddress(env, inputBuffer);
+    if (input == NULL) {
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+    jbyte *output = (*env)->GetPrimitiveArrayCritical(env, outputArray, 0);
+    if (output == NULL) {
+        if (outputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
+    }
+
+    jlong retVal = doDeflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen,
+            flush);
+
+    (*env)->ReleasePrimitiveArrayCritical(env, outputArray, input, 0);
+
+    return retVal;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Deflater_deflateBufferBuffer(JNIEnv *env, jobject this, 
jlong addr,
+                                         jobject inputBuffer, jint inputOff, 
jint inputLen,
+                                         jobject outputBuffer, jint outputOff, 
jint outputLen,
+                                         jint flush)
+{
+    jbyte *input = (*env)->GetDirectBufferAddress(env, inputBuffer);
+    if (input == NULL) {
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+    jbyte *output = (*env)->GetDirectBufferAddress(env, outputBuffer);
+    if (output == NULL) {
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+
+    return doDeflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen,
+            flush);
 }
 
 JNIEXPORT jint JNICALL
diff --git a/src/java.base/share/native/libzip/Inflater.c 
b/src/java.base/share/native/libzip/Inflater.c
index 2e21d084b39..5e79603cda7 100644
--- a/src/java.base/share/native/libzip/Inflater.c
+++ b/src/java.base/share/native/libzip/Inflater.c
@@ -44,7 +44,6 @@
 
 static jfieldID needDictID;
 static jfieldID finishedID;
-static jfieldID bufID, offID, lenID;
 
 JNIEXPORT void JNICALL
 Java_java_util_zip_Inflater_initIDs(JNIEnv *env, jclass cls)
@@ -53,12 +52,6 @@ Java_java_util_zip_Inflater_initIDs(JNIEnv *env, jclass cls)
     CHECK_NULL(needDictID);
     finishedID = (*env)->GetFieldID(env, cls, "finished", "Z");
     CHECK_NULL(finishedID);
-    bufID = (*env)->GetFieldID(env, cls, "buf", "[B");
-    CHECK_NULL(bufID);
-    offID = (*env)->GetFieldID(env, cls, "off", "I");
-    CHECK_NULL(offID);
-    lenID = (*env)->GetFieldID(env, cls, "len", "I");
-    CHECK_NULL(lenID);
 }
 
 JNIEXPORT jlong JNICALL
@@ -94,16 +87,10 @@ Java_java_util_zip_Inflater_init(JNIEnv *env, jclass cls, 
jboolean nowrap)
     }
 }
 
-JNIEXPORT void JNICALL
-Java_java_util_zip_Inflater_setDictionary(JNIEnv *env, jclass cls, jlong addr,
-                                          jarray b, jint off, jint len)
+static void doSetDictionary(JNIEnv *env, jlong addr, jbyte *buf, jint off,
+                            jint len)
 {
-    Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
-    int res;
-    if (buf == 0) /* out of memory */
-        return;
-    res = inflateSetDictionary(jlong_to_ptr(addr), buf + off, len);
-    (*env)->ReleasePrimitiveArrayCritical(env, b, buf, 0);
+    int res = inflateSetDictionary(jlong_to_ptr(addr), (Bytef *) (buf + off), 
len);
     switch (res) {
     case Z_OK:
         break;
@@ -117,68 +104,172 @@ Java_java_util_zip_Inflater_setDictionary(JNIEnv *env, 
jclass cls, jlong addr,
     }
 }
 
-JNIEXPORT jint JNICALL
-Java_java_util_zip_Inflater_inflateBytes(JNIEnv *env, jobject this, jlong addr,
-                                         jarray b, jint off, jint len)
+JNIEXPORT void JNICALL
+Java_java_util_zip_Inflater_setDictionary(JNIEnv *env, jclass cls, jlong addr,
+                                          jbyteArray b, jint off, jint len)
+{
+    jbyte *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0);
+    if (buf == NULL) /* out of memory */
+        return;
+    doSetDictionary(env, addr, buf, off, len);
+    (*env)->ReleasePrimitiveArrayCritical(env, b, buf, 0);
+}
+
+JNIEXPORT void JNICALL
+Java_java_util_zip_Inflater_setDictionaryBuffer(JNIEnv *env, jclass cls, jlong 
addr,
+                                          jobject buffer, jint off, jint len)
+{
+    jbyte *buf = (*env)->GetDirectBufferAddress(env, buffer);
+    if (buf == NULL) { /* ??? */
+        JNU_ThrowInternalError(env, "should not reach here");
+        return;
+    }
+    doSetDictionary(env, addr, buf, off, len);
+}
+
+static jlong doInflate(JNIEnv *env, jobject this, jlong addr,
+                       jbyte *input, jint inputOff, jint inputLen,
+                       jbyte *output, jint outputOff, jint outputLen)
 {
     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);
+    jint inputUsed = 0, outputUsed = 0;
 
-    jbyte *in_buf;
-    jbyte *out_buf;
-    int ret;
+    strm->next_in  = (Bytef *) (input + inputOff);
+    strm->next_out = (Bytef *) (output + outputOff);
+    strm->avail_in  = inputLen;
+    strm->avail_out = outputLen;
 
-    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_PARTIAL_FLUSH);
-    (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0);
-    (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0);
+    int ret = inflate(strm, Z_PARTIAL_FLUSH);
 
     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);
+        inputUsed = inputLen - strm->avail_in;
+        outputUsed = outputLen - strm->avail_out;
+        break;
     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;
+        inputUsed = inputLen - strm->avail_in;
+        break;
     case Z_BUF_ERROR:
-        return 0;
+        break;
     case Z_DATA_ERROR:
         ThrowDataFormatException(env, strm->msg);
-        return 0;
+        break;
     case Z_MEM_ERROR:
         JNU_ThrowOutOfMemoryError(env, 0);
-        return 0;
+        break;
     default:
         JNU_ThrowInternalError(env, strm->msg);
-        return 0;
+        break;
+    }
+    return ((jlong)inputUsed) | (((jlong)outputUsed) << 31);
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Inflater_inflateBytesBytes(JNIEnv *env, jobject this, jlong 
addr,
+                                         jbyteArray inputArray, jint inputOff, 
jint inputLen,
+                                         jbyteArray outputArray, jint 
outputOff, jint outputLen)
+{
+    jbyte *input = (*env)->GetPrimitiveArrayCritical(env, inputArray, 0);
+    if (input == NULL) {
+        if (inputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
+    }
+    jbyte *output = (*env)->GetPrimitiveArrayCritical(env, outputArray, 0);
+    if (output == NULL) {
+        (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+        if (outputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
     }
+
+    jlong retVal = doInflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen);
+
+    (*env)->ReleasePrimitiveArrayCritical(env, outputArray, output, 0);
+    (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+
+    return retVal;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Inflater_inflateBytesBuffer(JNIEnv *env, jobject this, 
jlong addr,
+                                         jbyteArray inputArray, jint inputOff, 
jint inputLen,
+                                         jobject outputBuffer, jint outputOff, 
jint outputLen)
+{
+    jbyte *input = (*env)->GetPrimitiveArrayCritical(env, inputArray, 0);
+    if (input == NULL) {
+        if (inputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
+    }
+    jbyte *output = (*env)->GetDirectBufferAddress(env, outputBuffer);
+    if (output == NULL) {
+        (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+
+    jlong retVal = doInflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen);
+
+    (*env)->ReleasePrimitiveArrayCritical(env, inputArray, input, 0);
+
+    return retVal;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Inflater_inflateBufferBytes(JNIEnv *env, jobject this, 
jlong addr,
+                                         jobject inputBuffer, jint inputOff, 
jint inputLen,
+                                         jbyteArray outputArray, jint 
outputOff, jint outputLen)
+{
+    jbyte *input = (*env)->GetDirectBufferAddress(env, inputBuffer);
+    if (input == NULL) {
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+    jbyte *output = (*env)->GetPrimitiveArrayCritical(env, outputArray, 0);
+    if (output == NULL) {
+        if (outputLen != 0 && (*env)->ExceptionOccurred(env) == NULL)
+            JNU_ThrowOutOfMemoryError(env, 0);
+        return 0L;
+    }
+
+    jlong retVal = doInflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen);
+
+    (*env)->ReleasePrimitiveArrayCritical(env, outputArray, input, 0);
+
+    return retVal;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_Inflater_inflateBufferBuffer(JNIEnv *env, jobject this, 
jlong addr,
+                                         jobject inputBuffer, jint inputOff, 
jint inputLen,
+                                         jobject outputBuffer, jint outputOff, 
jint outputLen)
+{
+    jbyte *input = (*env)->GetDirectBufferAddress(env, inputBuffer);
+    if (input == NULL) {
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+    jbyte *output = (*env)->GetDirectBufferAddress(env, outputBuffer);
+    if (output == NULL) {
+        JNU_ThrowInternalError(env, "should not reach here");
+        return 0L;
+    }
+
+    return doInflate(env, this, addr,
+            input, inputOff, inputLen,
+            output, outputOff, outputLen);
 }
 
 JNIEXPORT jint JNICALL
diff --git a/test/jdk/java/util/zip/FlaterTest.java 
b/test/jdk/java/util/zip/FlaterTest.java
index 7245440d033..b2e01c3e7cb 100644
--- a/test/jdk/java/util/zip/FlaterTest.java
+++ b/test/jdk/java/util/zip/FlaterTest.java
@@ -41,7 +41,7 @@ import java.util.zip.*;
  */
 public class FlaterTest extends Thread {
     private static final int DATA_LEN = 1024 * 128;
-    private static byte[] data;
+    private static ByteBuffer data;
 
     // If true, print extra info.
     private static final boolean debug = false;
@@ -51,15 +51,13 @@ public class FlaterTest extends Thread {
         Collections.synchronizedSet(new HashSet());
 
     /** Fill in {@code data} with random values. */
-    static void createData() {
-        ByteBuffer bb = ByteBuffer.allocate(8);
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        for (int i = 0; i < DATA_LEN; i++) {
-            bb.putDouble(0, Math.random());
-            baos.write(bb.array(), 0, 8);
+    static void createData(boolean direct) {
+        ByteBuffer bb = direct ? ByteBuffer.allocateDirect(DATA_LEN * 8) : 
ByteBuffer.allocate(DATA_LEN * 8);
+        for (int i = 0; i < DATA_LEN * 8; i += 8) {
+            bb.putDouble(i, Math.random());
         }
-        data = baos.toByteArray();
-        if (debug) System.out.println("data length is " + data.length);
+        data = bb;
+        if (debug) System.out.println("data length is " + data.capacity());
     }
 
     /** @return the length of the deflated {@code data}. */
@@ -68,7 +66,7 @@ public class FlaterTest extends Thread {
         Deflater deflater = new Deflater();
         deflater.setInput(data);
         deflater.finish();
-        byte[] out = new byte[data.length];
+        byte[] out = new byte[data.capacity()];
         rc = deflater.deflate(out);
         deflater.end();
         if (debug) System.out.println("deflatedLength is " + rc);
@@ -78,34 +76,40 @@ public class FlaterTest extends Thread {
     /** Compares given bytes with those in {@code data}.
      * @throws Exception if given bytes don't match {@code data}.
      */
-    private static void validate(byte[] buf, int offset, int len) throws 
Exception {
+    private static void validate(ByteBuffer buf, int offset, int len) throws 
Exception {
         for (int i = 0; i < len; i++ ) {
-            if (buf[i] != data[offset+i]) {
+            if (buf.get(i) != data.get(offset+i)) {
                 throw new Exception("mismatch at " + (offset + i));
             }
         }
     }
 
     public static void realMain(String[] args) throws Throwable {
-        createData();
         int numThreads = args.length > 0 ? Integer.parseInt(args[0]) : 5;
-        new FlaterTest().go(numThreads);
+        createData(false);
+        new FlaterTest().go(numThreads, false);
+        new FlaterTest().go(numThreads, true);
+        createData(true);
+        new FlaterTest().go(numThreads, false);
+        new FlaterTest().go(numThreads, true);
     }
 
-    private synchronized void go(int numThreads) throws Throwable {
+    private synchronized void go(int numThreads, boolean direct) throws 
Throwable {
         int deflatedLength = getDeflatedLength();
 
         long time = System.currentTimeMillis();
         for (int i = 0; i < numThreads; i++) {
-            Flater f = new Flater(deflatedLength);
+            Flater f = new Flater(deflatedLength, direct);
             flaters.add(f);
             f.start();
         }
-        while (flaters.size() != 0) {
-            try {
-                Thread.currentThread().sleep(10);
-            } catch (InterruptedException ex) {
-                unexpected(ex);
+        synchronized (flaters) {
+            while (flaters.size() != 0) {
+                try {
+                    flaters.wait();
+                } catch (InterruptedException ex) {
+                    unexpected(ex);
+                }
             }
         }
         time = System.currentTimeMillis() - time;
@@ -116,32 +120,41 @@ public class FlaterTest extends Thread {
     /** Deflates and inflates data. */
     static class Flater extends Thread {
         private final int deflatedLength;
+        private final boolean direct;
 
-        private Flater(int length) {
+        private Flater(int length, final boolean direct) {
             this.deflatedLength = length;
+            this.direct = direct;
         }
 
         /** Deflates and inflates {@code data}. */
         public void run() {
             if (debug) System.out.println(getName() + " starting run()");
             try {
-                byte[] deflated = DeflateData(deflatedLength);
+                ByteBuffer deflated = DeflateData(deflatedLength);
                 InflateData(deflated);
             } catch (Throwable t) {
                 t.printStackTrace();
                 fail(getName() + " failed");
             } finally {
-                flaters.remove(this);
+                synchronized (flaters) {
+                    flaters.remove(this);
+                    if (flaters.isEmpty()) {
+                        flaters.notifyAll();
+                    }
+                }
             }
         }
 
         /** Returns a copy of {@code data} in deflated form. */
-        private byte[] DeflateData(int length) throws Throwable {
+        private ByteBuffer DeflateData(int length) throws Throwable {
             Deflater deflater = new Deflater();
+            data.clear();
             deflater.setInput(data);
             deflater.finish();
-            byte[] out = new byte[length];
+            ByteBuffer out = direct ? ByteBuffer.allocateDirect(length) : 
ByteBuffer.allocate(length);
             deflater.deflate(out);
+            out.flip();
             return out;
         }
 
@@ -149,14 +162,15 @@ public class FlaterTest extends Thread {
          * inflation.
          * @throws Exception if inflated bytes don't match {@code data}.
          */
-        private void InflateData(byte[] bytes) throws Throwable {
+        private void InflateData(ByteBuffer bytes) throws Throwable {
             Inflater inflater = new Inflater();
-            inflater.setInput(bytes, 0, bytes.length);
+            inflater.setInput(bytes);
             int len = 1024 * 8;
             int offset = 0;
+            ByteBuffer buf = direct ? ByteBuffer.allocateDirect(len) : 
ByteBuffer.allocate(len);
             while (inflater.getRemaining() > 0) {
-                byte[] buf = new byte[len];
-                int inflated = inflater.inflate(buf, 0, len);
+                buf.clear();
+                int inflated = inflater.inflate(buf);
                 validate(buf, offset, inflated);
                 offset += inflated;
             }

Reply via email to