aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlmaz Mingaleev <mingaleev@google.com>2024-02-27 10:16:01 +0000
committerAlmaz Mingaleev <mingaleev@google.com>2024-02-27 10:16:01 +0000
commitab03ae0edd58d170f101f83fe12848436b00cefe (patch)
treea6ca96e1d6df9ae5bbe08b99feaca3a141329ee7
parentf1dc3e1eac7ab8fd0fc2eebcf36fe6295dc6d0d8 (diff)
downloadlibcore-ab03ae0edd58d170f101f83fe12848436b00cefe.tar.gz
Import test.java.util.zip from jdk-17.0.10-ga
List of files: ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java ojluni/src/test/java/util/zip/DeInflate.java ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java Generated by tools/expected_upstream/ojluni_merge_to_main.py Bug: 254213857 Test: N/A No-Typo-Check: Imported files Change-Id: Ia7fc95cdb5bf1551c4a24e68b65d36f799b537c2
-rw-r--r--EXPECTED_UPSTREAM4
-rw-r--r--ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java209
-rw-r--r--ojluni/src/test/java/util/zip/DeInflate.java333
-rw-r--r--ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java252
-rw-r--r--ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java156
5 files changed, 954 insertions, 0 deletions
diff --git a/EXPECTED_UPSTREAM b/EXPECTED_UPSTREAM
index 9957c025eb8..419cbfd28aa 100644
--- a/EXPECTED_UPSTREAM
+++ b/EXPECTED_UPSTREAM
@@ -2612,11 +2612,15 @@ ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/Spliterat
ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/DefaultMethodStreams.java,jdk17u/jdk-17.0.6-ga,test/jdk/lib/testlibrary/bootlib/java.base/java/util/stream/DefaultMethodStreams.java
ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/ThrowableHelper.java,jdk17u/jdk-17.0.6-ga,test/jdk/lib/testlibrary/bootlib/java.base/java/util/stream/ThrowableHelper.java
ojluni/src/test/java/util/zip/ChecksumBase.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/ChecksumBase.java
+ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/CloseInflaterDeflaterTest.java
+ojluni/src/test/java/util/zip/DeInflate.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/DeInflate.java
+ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/DeflaterDictionaryTests.java
ojluni/src/test/java/util/zip/TestCRC32.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestCRC32.java
ojluni/src/test/java/util/zip/TestCRC32C.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestCRC32C.java
ojluni/src/test/java/util/zip/TestChecksum.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestChecksum.java
ojluni/src/test/java/util/zip/TestExtraTime.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestExtraTime.java
ojluni/src/test/java/util/zip/TestLocalTime.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestLocalTime.java
+ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/ZipFile/TestCleaner.java
ojluni/src/test/java/util/zip/ZipFile/Zip64SizeTest.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/util/zip/ZipFile/Zip64SizeTest.java
ojluni/src/tools/make/gensrc/GensrcBuffer.gmk,jdk17u/jdk-17.0.6-ga,make/modules/java.base/gensrc/GensrcBuffer.gmk
ojluni/src/tools/make/gensrc/GensrcCharsetCoder.gmk,jdk17u/jdk-17.0.6-ga,make/modules/java.base/gensrc/GensrcCharsetCoder.gmk
diff --git a/ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java b/ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java
new file mode 100644
index 00000000000..b8f91971060
--- /dev/null
+++ b/ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2021, 2022, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8193682 8278794 8284771
+ * @summary Test Infinite loop while writing on closed Deflater and Inflater.
+ * @run testng CloseInflaterDeflaterTest
+ */
+import java.io.*;
+import java.util.Random;
+import java.util.jar.JarOutputStream;
+import java.util.zip.DeflaterInputStream;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.GZIPOutputStream;
+import java.util.zip.InflaterOutputStream;
+import java.util.zip.ZipOutputStream;
+import java.util.zip.ZipEntry;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertThrows;
+
+
+public class CloseInflaterDeflaterTest {
+
+ // Number of bytes to write/read from Deflater/Inflater
+ private static final int INPUT_LENGTH= 512;
+ // OutputStream that will throw an exception during a write operation
+ private static OutputStream outStream = new OutputStream() {
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ throw new IOException();
+ }
+ @Override
+ public void write(byte[] b) throws IOException {}
+ @Override
+ public void write(int b) throws IOException {}
+ };
+ // InputStream that will throw an exception during a read operation
+ private static InputStream inStream = new InputStream() {
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ throw new IOException();
+ }
+ @Override
+ public int read(byte[] b) throws IOException { throw new IOException();}
+ @Override
+ public int read() throws IOException { throw new IOException();}
+ };
+ // Input bytes for read/write operation
+ private static byte[] inputBytes = new byte[INPUT_LENGTH];
+ // Random function to add bytes to inputBytes
+ private static Random rand = new Random();
+
+ /**
+ * DataProvider to specify whether to use close() or finish() of OutputStream
+ *
+ * @return Entry object indicating which method to use for closing OutputStream
+ */
+ @DataProvider
+ public Object[][] testOutputStreams() {
+ return new Object[][] {
+ { true },
+ { false },
+ };
+ }
+
+ /**
+ * DataProvider to specify on which outputstream closeEntry() has to be called
+ *
+ * @return Entry object returning either JarOutputStream or ZipOutputStream
+ */
+ @DataProvider
+ public Object[][] testZipAndJar() throws IOException{
+ return new Object[][] {
+ { new JarOutputStream(outStream)},
+ { new ZipOutputStream(outStream)},
+ };
+ }
+
+ /**
+ * Add inputBytes array with random bytes to write into OutputStream
+ */
+ @BeforeTest
+ public void before_test()
+ {
+ rand.nextBytes(inputBytes);
+ }
+
+ /**
+ * Test for infinite loop by writing bytes to closed GZIPOutputStream
+ *
+ * @param useCloseMethod indicates whether to use Close() or finish() method
+ * @throws IOException if an error occurs
+ */
+ @Test(dataProvider = "testOutputStreams")
+ public void testGZip(boolean useCloseMethod) throws IOException {
+ GZIPOutputStream gzip = new GZIPOutputStream(outStream);
+ gzip.write(inputBytes, 0, INPUT_LENGTH);
+ assertThrows(IOException.class, () -> {
+ // Close GZIPOutputStream
+ if (useCloseMethod) {
+ gzip.close();
+ } else {
+ gzip.finish();
+ }
+ });
+ // Write on a closed GZIPOutputStream, closed Deflater IOException expected
+ assertThrows(NullPointerException.class , () -> gzip.write(inputBytes, 0, INPUT_LENGTH));
+ }
+
+ /**
+ * Test for infinite loop by writing bytes to closed DeflaterOutputStream
+ *
+ * @param useCloseMethod indicates whether to use Close() or finish() method
+ * @throws IOException if an error occurs
+ */
+ @Test(dataProvider = "testOutputStreams")
+ public void testDeflaterOutputStream(boolean useCloseMethod) throws IOException {
+ DeflaterOutputStream def = new DeflaterOutputStream(outStream);
+ assertThrows(IOException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH));
+ assertThrows(IOException.class, () -> {
+ // Close DeflaterOutputStream
+ if (useCloseMethod) {
+ def.close();
+ } else {
+ def.finish();
+ }
+ });
+ // Write on a closed DeflaterOutputStream, 'Deflater has been closed' NPE is expected
+ assertThrows(NullPointerException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH));
+ }
+
+ /**
+ * Test for infinite loop by reading bytes from closed DeflaterInputStream
+ *
+ * @throws IOException if an error occurs
+ */
+ @Test
+ public void testDeflaterInputStream() throws IOException {
+ DeflaterInputStream def = new DeflaterInputStream(inStream);
+ assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH));
+ // Close DeflaterInputStream
+ def.close();
+ // Read from a closed DeflaterInputStream, closed Deflater IOException expected
+ assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH));
+ }
+
+ /**
+ * Test for infinite loop by writing bytes to closed InflaterOutputStream
+ *
+ * Note: Disabling this test as it is failing intermittently.
+ * @param useCloseMethod indicates whether to use Close() or finish() method
+ * @throws IOException if an error occurs
+ */
+ @Test(dataProvider = "testOutputStreams",enabled=false)
+ public void testInflaterOutputStream(boolean useCloseMethod) throws IOException {
+ InflaterOutputStream inf = new InflaterOutputStream(outStream);
+ assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH));
+ assertThrows(IOException.class , () -> {
+ // Close InflaterOutputStream
+ if (useCloseMethod) {
+ inf.close();
+ } else {
+ inf.finish();
+ }
+ });
+ // Write on a closed InflaterOutputStream , closed Inflater IOException expected
+ assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH));
+ }
+
+ /**
+ * Test for infinite loop by writing bytes to closed ZipOutputStream/JarOutputStream
+ *
+ * @param zip will be the instance of either JarOutputStream or ZipOutputStream
+ * @throws IOException if an error occurs
+ */
+ @Test(dataProvider = "testZipAndJar")
+ public void testZipCloseEntry(ZipOutputStream zip) throws IOException {
+ assertThrows(IOException.class , () -> zip.putNextEntry(new ZipEntry("")));
+ zip.write(inputBytes, 0, INPUT_LENGTH);
+ assertThrows(IOException.class , () -> zip.closeEntry());
+ // Write on a closed ZipOutputStream , 'Deflater has been closed' NPE is expected
+ assertThrows(NullPointerException.class , () -> zip.write(inputBytes, 0, INPUT_LENGTH));
+ }
+
+}
diff --git a/ojluni/src/test/java/util/zip/DeInflate.java b/ojluni/src/test/java/util/zip/DeInflate.java
new file mode 100644
index 00000000000..d3785f0360e
--- /dev/null
+++ b/ojluni/src/test/java/util/zip/DeInflate.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2011, 2023, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 7110149 8184306 6341887
+ * @summary Test basic deflater & inflater functionality
+ * @key randomness
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import java.util.zip.*;
+
+
+public class DeInflate {
+
+ private static Random rnd = new Random();
+
+
+ static void checkStream(Deflater def, byte[] in, int len,
+ byte[] out1, byte[] out2, boolean nowrap)
+ throws Throwable
+ {
+ Arrays.fill(out1, (byte)0);
+ Arrays.fill(out2, (byte)0);
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ try (DeflaterOutputStream defos = new DeflaterOutputStream(baos, def)) {
+ defos.write(in, 0, len);
+ }
+ out1 = baos.toByteArray();
+ }
+ int m = out1.length;
+
+ Inflater inf = new Inflater(nowrap);
+ inf.setInput(out1, 0, m);
+ int n = inf.inflate(out2);
+
+ if (n != len ||
+ !Arrays.equals(Arrays.copyOf(in, len), Arrays.copyOf(out2, len)) ||
+ inf.inflate(out2) != 0) {
+ System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
+ m, n, len, Arrays.equals(in, out2));
+ throw new RuntimeException("De/inflater failed:" + def);
+ }
+ }
+
+ static void checkByteBuffer(Deflater def, Inflater inf,
+ ByteBuffer in, ByteBuffer out1, ByteBuffer out2,
+ byte[] expected, int len, byte[] result,
+ boolean out1ReadOnlyWhenInflate)
+ throws Throwable {
+ def.reset();
+ inf.reset();
+
+ def.setInput(in);
+ def.finish();
+ int m = def.deflate(out1);
+
+ out1.flip();
+ if (out1ReadOnlyWhenInflate)
+ out1 = out1.asReadOnlyBuffer();
+ inf.setInput(out1);
+ int n = inf.inflate(out2);
+
+ out2.flip();
+ out2.get(result, 0, n);
+
+ if (n != len || out2.position() != len ||
+ !Arrays.equals(Arrays.copyOf(expected, len), Arrays.copyOf(result, len)) ||
+ inf.inflate(result) != 0) {
+ throw new RuntimeException("De/inflater(buffer) failed:" + def);
+ }
+ }
+
+ static void checkByteBufferReadonly(Deflater def, Inflater inf,
+ ByteBuffer in, ByteBuffer out1, ByteBuffer out2)
+ throws Throwable {
+ def.reset();
+ inf.reset();
+ def.setInput(in);
+ def.finish();
+ int m = -1;
+ if (!out2.isReadOnly())
+ out2 = out2.asReadOnlyBuffer();
+ try {
+ m = def.deflate(out2);
+ throw new RuntimeException("deflater: ReadOnlyBufferException: failed");
+ } catch (ReadOnlyBufferException robe) {}
+ m = def.deflate(out1);
+ out1.flip();
+ inf.setInput(out1);
+ try {
+ inf.inflate(out2);
+ throw new RuntimeException("inflater: ReadOnlyBufferException: failed");
+ } catch (ReadOnlyBufferException robe) {}
+ }
+
+ /**
+ * Uses the {@code def} deflater to deflate the input data {@code in} of length {@code len}.
+ * A new {@link Inflater} is then created within this method to inflate the deflated data. The
+ * inflated data is then compared with the {@code in} to assert that it matches the original
+ * input data.
+ * This method repeats these checks for the different overloaded methods of
+ * {@code Deflater.deflate(...)} and {@code Inflater.inflate(...)}
+ *
+ * @param def the deflater to use for deflating the contents in {@code in}
+ * @param in the input content
+ * @param len the length of the input content to use
+ * @param nowrap will be passed to the constructor of the {@code Inflater} used in this
+ * method
+ * @throws Throwable if any error occurs during the check
+ */
+ static void check(Deflater def, byte[] in, int len, boolean nowrap)
+ throws Throwable
+ {
+ byte[] tempBuffer = new byte[1024];
+ byte[] out1, out2;
+ int m = 0, n = 0;
+ Inflater inf = new Inflater(nowrap);
+ def.setInput(in, 0, len);
+ def.finish();
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ while (!def.finished()) {
+ int temp_counter = def.deflate(tempBuffer);
+ m += temp_counter;
+ baos.write(tempBuffer, 0, temp_counter);
+ }
+ out1 = baos.toByteArray();
+ baos.reset();
+
+ inf.setInput(out1, 0, m);
+
+ while (!inf.finished()) {
+ int temp_counter = inf.inflate(tempBuffer);
+ n += temp_counter;
+ baos.write(tempBuffer, 0, temp_counter);
+ }
+ out2 = baos.toByteArray();
+ if (n != len ||
+ !Arrays.equals(in, 0, len, out2, 0, len) ||
+ inf.inflate(out2) != 0) {
+ System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
+ m, n, len, Arrays.equals(in, out2));
+ throw new RuntimeException("De/inflater failed:" + def);
+ }
+ }
+
+ // readable
+ Arrays.fill(out1, (byte)0);
+ Arrays.fill(out2, (byte)0);
+ ByteBuffer bbIn = ByteBuffer.wrap(in, 0, len);
+ ByteBuffer bbOut1 = ByteBuffer.wrap(out1);
+ ByteBuffer bbOut2 = ByteBuffer.wrap(out2);
+ checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
+ checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
+
+ // readonly in
+ Arrays.fill(out1, (byte)0);
+ Arrays.fill(out2, (byte)0);
+ bbIn = ByteBuffer.wrap(in, 0, len).asReadOnlyBuffer();
+ bbOut1 = ByteBuffer.wrap(out1);
+ bbOut2 = ByteBuffer.wrap(out2);
+ checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
+ checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
+
+ // readonly out1 when inflate
+ Arrays.fill(out1, (byte)0);
+ Arrays.fill(out2, (byte)0);
+ bbIn = ByteBuffer.wrap(in, 0, len);
+ bbOut1 = ByteBuffer.wrap(out1);
+ bbOut2 = ByteBuffer.wrap(out2);
+ checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, true);
+ checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
+
+ // direct
+ bbIn = ByteBuffer.allocateDirect(in.length);
+ bbIn.put(in, 0, n).flip();
+ bbOut1 = ByteBuffer.allocateDirect(out1.length);
+ bbOut2 = ByteBuffer.allocateDirect(out2.length);
+ checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
+ checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
+ }
+
+ static void checkDict(Deflater def, Inflater inf, byte[] src,
+ byte[] dstDef, byte[] dstInf,
+ ByteBuffer dictDef, ByteBuffer dictInf) throws Throwable {
+ def.reset();
+ inf.reset();
+
+ def.setDictionary(dictDef);
+ def.setInput(src);
+ def.finish();
+ int n = def.deflate(dstDef);
+
+ inf.setInput(dstDef, 0, n);
+ n = inf.inflate(dstInf);
+ if (n != 0 || !inf.needsDictionary()) {
+ throw new RuntimeException("checkDict failed: need dict to continue");
+ }
+ inf.setDictionary(dictInf);
+ n = inf.inflate(dstInf);
+ // System.out.println("result: " + new String(dstInf, 0, n));
+ if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
+ throw new RuntimeException("checkDict failed: inflate result");
+ }
+ }
+
+ static void checkDict(int level, int strategy) throws Throwable {
+
+ Deflater def = newDeflater(level, strategy, false, new byte[0]);
+ Inflater inf = new Inflater();
+
+ byte[] src = "hello world, hello world, hello sherman".getBytes();
+ byte[] dict = "hello".getBytes();
+
+ byte[] dstDef = new byte[1024];
+ byte[] dstInf = new byte[1024];
+
+ def.setDictionary(dict);
+ def.setInput(src);
+ def.finish();
+ int n = def.deflate(dstDef);
+
+ inf.setInput(dstDef, 0, n);
+ n = inf.inflate(dstInf);
+ if (n != 0 || !inf.needsDictionary()) {
+ throw new RuntimeException("checkDict failed: need dict to continue");
+ }
+ inf.setDictionary(dict);
+ n = inf.inflate(dstInf);
+ //System.out.println("result: " + new String(dstInf, 0, n));
+ if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
+ throw new RuntimeException("checkDict failed: inflate result");
+ }
+
+ ByteBuffer dictDef = ByteBuffer.wrap(dict);
+ ByteBuffer dictInf = ByteBuffer.wrap(dict);
+ checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
+
+ dictDef = ByteBuffer.allocateDirect(dict.length);
+ dictInf = ByteBuffer.allocateDirect(dict.length);
+ dictDef.put(dict).flip();
+ dictInf.put(dict).flip();
+ checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
+
+ def.end();
+ inf.end();
+ }
+
+ private static Deflater newDeflater(int level, int strategy, boolean dowrap, byte[] tmp) {
+ Deflater def = new Deflater(level, dowrap);
+ if (strategy != Deflater.DEFAULT_STRATEGY) {
+ def.setStrategy(strategy);
+ // The first invocation after setLevel/Strategy()
+ // with a different level/stragety returns 0, if
+ // there is no need to flush out anything for the
+ // previous setting/"data", this is tricky and
+ // appears un-documented.
+ def.deflate(tmp);
+ }
+ return def;
+ }
+
+ private static Deflater resetDeflater(Deflater def, int level, int strategy) {
+ def.setLevel(level);
+ def.setStrategy(strategy);
+ def.reset();
+ return def;
+ }
+
+ public static void main(String[] args) throws Throwable {
+
+ byte[] dataIn = new byte[1024 * 512];
+ rnd.nextBytes(dataIn);
+ byte[] dataOut1 = new byte[dataIn.length + 1024];
+ byte[] dataOut2 = new byte[dataIn.length];
+
+ Deflater defNotWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
+ Deflater defWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
+
+ for (int level = Deflater.DEFAULT_COMPRESSION;
+ level <= Deflater.BEST_COMPRESSION; level++) {
+ for (int strategy = Deflater.DEFAULT_STRATEGY;
+ strategy <= Deflater.HUFFMAN_ONLY; strategy++) {
+ for (boolean dowrap : new boolean[] { false, true }) {
+ System.out.println("level:" + level +
+ ", strategy: " + strategy +
+ ", dowrap: " + dowrap);
+ for (int i = 0; i < 5; i++) {
+ int len = (i == 0)? dataIn.length
+ : new Random().nextInt(dataIn.length);
+ System.out.println("iteration: " + (i + 1) + " input length: " + len);
+ // use a new deflater
+ Deflater def = newDeflater(level, strategy, dowrap, dataOut2);
+ check(def, dataIn, len, dowrap);
+ def.end();
+
+ // reuse the deflater (with reset) and test on stream, which
+ // uses a "smaller" buffer (smaller than the overall data)
+ def = resetDeflater(dowrap ? defWrap : defNotWrap, level, strategy);
+ checkStream(def, dataIn, len, dataOut1, dataOut2, dowrap);
+ }
+ }
+ // test setDictionary()
+ checkDict(level, strategy);
+ }
+ }
+ }
+}
diff --git a/ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java b/ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java
new file mode 100644
index 00000000000..17d2b735806
--- /dev/null
+++ b/ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.testng.Assert.assertThrows;
+
+/**
+ * @test
+ * @bug 8252739
+ * @summary Verify Deflater.setDictionary(dictionary, offset, length) uses the offset
+ * @run testng/othervm DeflaterDictionaryTests
+ */
+public class DeflaterDictionaryTests {
+ // Output buffer size
+ private static final int RESULT_SIZE = 1024;
+ // Data to compress
+ private static final String SRC_DATA = "Welcome to the US Open;".repeat(6);
+ // Dictionary to be used
+ private static final String DICTIONARY = "US Open";
+ private static final int DICTIONARY_OFFSET = 1;
+ private static final int DICTIONARY_LENGTH = 3;
+
+ /**
+ * DataProvider with offsets which should be valid for setDictionary
+ *
+ * @return valid offset values
+ */
+ @DataProvider(name = "validDictionaryOffsets")
+ protected Object[][] validDictionaryOffsets() {
+ return new Object[][]{
+ {0},
+ {DICTIONARY_OFFSET},
+ {DICTIONARY_LENGTH}
+ };
+ }
+
+ /**
+ * DataProvider with invalid offsets for setDictionary
+ *
+ * @return invalid offset values
+ */
+ @DataProvider(name = "invalidDictionaryOffsets")
+ protected Object[][] invalidDictionaryOffsets() {
+ return new Object[][]{
+ {-1},
+ {DICTIONARY_LENGTH + 2},
+ {DICTIONARY.length()}
+ };
+ }
+
+ /**
+ * Validate that an offset can be used with Deflater::setDictionary
+ *
+ * @param dictionary_offset offset value to be used
+ * @throws Exception if an error occurs
+ */
+ @Test(dataProvider = "validDictionaryOffsets")
+ public void testByteArray(int dictionary_offset) throws Exception {
+ byte[] input = SRC_DATA.getBytes(UTF_8);
+ byte[] output = new byte[RESULT_SIZE];
+ Deflater deflater = new Deflater();
+ Inflater inflater = new Inflater();
+ try {
+ // Compress the bytes
+ deflater.setDictionary(DICTIONARY.getBytes(UTF_8), dictionary_offset, DICTIONARY_LENGTH);
+ deflater.setInput(input);
+ deflater.finish();
+ int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH);
+ System.out.printf("Deflater::getTotalOut:%s, Deflater::getAdler: %s," +
+ " compressed length: %s%n", deflater.getTotalOut(),
+ deflater.getTotalOut(), compressedDataLength);
+ deflater.finished();
+
+ // Decompress the bytes
+ inflater.setInput(output, 0, compressedDataLength);
+ byte[] result = new byte[RESULT_SIZE];
+ int resultLength = inflater.inflate(result);
+ if (inflater.needsDictionary()) {
+ System.out.println("Specifying Dictionary");
+ inflater.setDictionary(DICTIONARY.getBytes(UTF_8), dictionary_offset, DICTIONARY_LENGTH);
+ resultLength = inflater.inflate(result);
+ } else {
+ System.out.println("Did not need to use a Dictionary");
+ }
+ inflater.finished();
+ System.out.printf("Inflater::getAdler:%s, length: %s%n",
+ inflater.getAdler(), resultLength);
+
+ Assert.assertEquals(SRC_DATA.length(), resultLength);
+ Assert.assertEquals(input, Arrays.copyOf(result, resultLength));
+ } finally {
+ // Release Resources
+ deflater.end();
+ inflater.end();
+ }
+ }
+
+ /**
+ * Validate that a ByteBuffer can be used with Deflater::setDictionary
+ *
+ * @throws Exception if an error occurs
+ */
+ @Test
+ public void testHeapByteBuffer() throws Exception {
+ byte[] input = SRC_DATA.getBytes(UTF_8);
+ byte[] output = new byte[RESULT_SIZE];
+ ByteBuffer dictDef = ByteBuffer.wrap(DICTIONARY.getBytes(UTF_8), DICTIONARY_OFFSET, DICTIONARY_LENGTH);
+ ByteBuffer dictInf = ByteBuffer.wrap(DICTIONARY.getBytes(UTF_8), DICTIONARY_OFFSET, DICTIONARY_LENGTH);
+ Deflater deflater = new Deflater();
+ Inflater inflater = new Inflater();
+ try {
+ // Compress the bytes
+ deflater.setDictionary(dictDef);
+ deflater.setInput(input);
+ deflater.finish();
+ int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH);
+ System.out.printf("Deflater::getTotalOut:%s, Deflater::getAdler: %s," +
+ " compressed length: %s%n", deflater.getTotalOut(),
+ deflater.getTotalOut(), compressedDataLength);
+ deflater.finished();
+
+ // Decompress the bytes
+ inflater.setInput(output, 0, compressedDataLength);
+ byte[] result = new byte[RESULT_SIZE];
+ int resultLength = inflater.inflate(result);
+ if (inflater.needsDictionary()) {
+ System.out.println("Specifying Dictionary");
+ inflater.setDictionary(dictInf);
+ resultLength = inflater.inflate(result);
+ } else {
+ System.out.println("Did not need to use a Dictionary");
+ }
+ inflater.finished();
+ System.out.printf("Inflater::getAdler:%s, length: %s%n",
+ inflater.getAdler(), resultLength);
+
+ Assert.assertEquals(SRC_DATA.length(), resultLength);
+ Assert.assertEquals(input, Arrays.copyOf(result, resultLength));
+ } finally {
+ // Release Resources
+ deflater.end();
+ inflater.end();
+ }
+ }
+
+ /**
+ * Validate that ByteBuffer::allocateDirect can be used with Deflater::setDictionary
+ *
+ * @throws Exception if an error occurs
+ */
+ @Test
+ public void testByteBufferDirect() throws Exception {
+ byte[] input = SRC_DATA.getBytes(UTF_8);
+ byte[] output = new byte[RESULT_SIZE];
+ ByteBuffer dictDef = ByteBuffer.allocateDirect(DICTIONARY.length());
+ ByteBuffer dictInf = ByteBuffer.allocateDirect(DICTIONARY.length());
+ dictDef.put(DICTIONARY.getBytes(UTF_8));
+ dictInf.put(DICTIONARY.getBytes(UTF_8));
+ dictDef.position(DICTIONARY_OFFSET);
+ dictDef.limit(DICTIONARY_LENGTH);
+ dictInf.position(DICTIONARY_OFFSET);
+ dictInf.limit(DICTIONARY_LENGTH);
+ Deflater deflater = new Deflater();
+ Inflater inflater = new Inflater();
+ try {
+ // Compress the bytes
+ deflater.setDictionary(dictDef.slice());
+ deflater.setInput(input);
+ deflater.finish();
+ int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH);
+ System.out.printf("Deflater::getTotalOut:%s, Deflater::getAdler: %s," +
+ " compressed length: %s%n", deflater.getTotalOut(),
+ deflater.getTotalOut(), compressedDataLength);
+ deflater.finished();
+
+ // Decompress the bytes
+ inflater.setInput(output, 0, compressedDataLength);
+ byte[] result = new byte[RESULT_SIZE];
+ int resultLength = inflater.inflate(result);
+ if (inflater.needsDictionary()) {
+ System.out.println("Specifying Dictionary");
+ inflater.setDictionary(dictInf.slice());
+ resultLength = inflater.inflate(result);
+ } else {
+ System.out.println("Did not need to use a Dictionary");
+ }
+ inflater.finished();
+ System.out.printf("Inflater::getAdler:%s, length: %s%n",
+ inflater.getAdler(), resultLength);
+
+ Assert.assertEquals(SRC_DATA.length(), resultLength);
+ Assert.assertEquals(input, Arrays.copyOf(result, resultLength));
+ } finally {
+ // Release Resources
+ deflater.end();
+ inflater.end();
+ }
+ }
+
+ /**
+ * Validate that an invalid offset used with setDictionary will
+ * throw an Exception
+ *
+ * @param dictionary_offset offset value to be used
+ */
+ @Test(dataProvider = "invalidDictionaryOffsets")
+ public void testInvalidOffsets(int dictionary_offset) {
+ byte[] dictionary = DICTIONARY.getBytes(UTF_8);
+
+ Deflater deflater = new Deflater();
+ Inflater inflater = new Inflater();
+ try {
+ assertThrows(ArrayIndexOutOfBoundsException.class, () ->
+ deflater.setDictionary(dictionary, dictionary_offset, DICTIONARY_LENGTH));
+ assertThrows(ArrayIndexOutOfBoundsException.class, () ->
+ inflater.setDictionary(dictionary, dictionary_offset, DICTIONARY_LENGTH));
+ } finally {
+ // Release Resources
+ deflater.end();
+ inflater.end();
+ }
+ }
+} \ No newline at end of file
diff --git a/ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java b/ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java
new file mode 100644
index 00000000000..d24aa5045fd
--- /dev/null
+++ b/ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8185582 8197989
+ * @modules java.base/java.util.zip:open java.base/jdk.internal.vm.annotation
+ * @summary Check the resources of Inflater, Deflater and ZipFile are always
+ * cleaned/released when the instance is not unreachable
+ */
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.zip.*;
+import jdk.internal.vm.annotation.DontInline;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+public class TestCleaner {
+
+ public static void main(String[] args) throws Throwable {
+ testDeInflater();
+ testZipFile();
+ }
+
+ private static long addrOf(Object obj) {
+ try {
+ Field addr = obj.getClass().getDeclaredField("address");
+ if (!addr.trySetAccessible()) {
+ return -1;
+ }
+ return addr.getLong(obj);
+ } catch (Exception x) {
+ return -1;
+ }
+ }
+
+ // verify the "native resource" of In/Deflater has been cleaned
+ private static void testDeInflater() throws Throwable {
+ Field zsRefDef = Deflater.class.getDeclaredField("zsRef");
+ Field zsRefInf = Inflater.class.getDeclaredField("zsRef");
+ if (!zsRefDef.trySetAccessible() || !zsRefInf.trySetAccessible()) {
+ throw new RuntimeException("'zsRef' is not accesible");
+ }
+ if (addrOf(zsRefDef.get(new Deflater())) == -1 ||
+ addrOf(zsRefInf.get(new Inflater())) == -1) {
+ throw new RuntimeException("'addr' is not accesible");
+ }
+ List<Object> list = new ArrayList<>();
+ byte[] buf1 = new byte[1024];
+ byte[] buf2 = new byte[1024];
+ for (int i = 0; i < 10; i++) {
+ var def = new Deflater();
+ list.add(zsRefDef.get(def));
+ def.setInput("hello".getBytes());
+ def.finish();
+ int n = def.deflate(buf1);
+
+ var inf = new Inflater();
+ list.add(zsRefInf.get(inf));
+ inf.setInput(buf1, 0, n);
+ n = inf.inflate(buf2);
+ if (!"hello".equals(new String(buf2, 0, n))) {
+ throw new RuntimeException("compression/decompression failed");
+ }
+ }
+
+ int n = 10;
+ long cnt = list.size();
+ while (n-- > 0 && cnt != 0) {
+ Thread.sleep(100);
+ System.gc();
+ cnt = list.stream().filter(o -> addrOf(o) != 0).count();
+ }
+ if (cnt != 0)
+ throw new RuntimeException("cleaner failed to clean : " + cnt);
+
+ }
+
+ @DontInline
+ private static Object openAndCloseZipFile(File zip) throws Throwable {
+ try {
+ try (var fos = new FileOutputStream(zip);
+ var zos = new ZipOutputStream(fos)) {
+ zos.putNextEntry(new ZipEntry("hello"));
+ zos.write("hello".getBytes(US_ASCII));
+ zos.closeEntry();
+ }
+
+ var zf = new ZipFile(zip);
+ var es = zf.entries();
+ while (es.hasMoreElements()) {
+ zf.getInputStream(es.nextElement()).read();
+ }
+
+ Field fieldRes = ZipFile.class.getDeclaredField("res");
+ if (!fieldRes.trySetAccessible()) {
+ throw new RuntimeException("'ZipFile.res' is not accesible");
+ }
+ Object zfRes = fieldRes.get(zf);
+ if (zfRes == null) {
+ throw new RuntimeException("'ZipFile.res' is null");
+ }
+ Field fieldZsrc = zfRes.getClass().getDeclaredField("zsrc");
+ if (!fieldZsrc.trySetAccessible()) {
+ throw new RuntimeException("'ZipFile.zsrc' is not accesible");
+ }
+ return fieldZsrc.get(zfRes);
+ } finally {
+ zip.delete();
+ }
+ }
+
+
+ private static void testZipFile() throws Throwable {
+ File dir = new File(System.getProperty("test.dir", "."));
+ File zip = File.createTempFile("testzf", "zip", dir);
+
+ Object zsrc = openAndCloseZipFile(zip);
+ if (zsrc != null) {
+ Field zfileField = zsrc.getClass().getDeclaredField("zfile");
+ if (!zfileField.trySetAccessible()) {
+ throw new RuntimeException("'ZipFile.Source.zfile' is not accesible");
+ }
+ //System.out.println("zffile: " + zfileField.get(zsrc));
+ int n = 10;
+ while (n-- > 0 && zfileField.get(zsrc) != null) {
+ System.out.println("waiting gc ... " + n);
+ System.gc();
+ Thread.sleep(100);
+ }
+ if (zfileField.get(zsrc) != null) {
+ throw new RuntimeException("cleaner failed to clean zipfile.");
+ }
+ }
+ }
+}