1 /*
2  * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /**
25  * @test
26  * @bug 7110149 8184306 6341887
27  * @summary Test basic deflater & inflater functionality
28  * @key randomness
29  */
30 
31 package test.java.util.zip;
32 
33 import java.io.*;
34 import java.nio.*;
35 import java.util.*;
36 import java.util.zip.*;
37 
38 
39 public class DeInflate {
40 
41     private static Random rnd = new Random();
42 
43 
checkStream(Deflater def, byte[] in, int len, byte[] out1, byte[] out2, boolean nowrap)44     static void checkStream(Deflater def, byte[] in, int len,
45                             byte[] out1, byte[] out2, boolean nowrap)
46         throws Throwable
47     {
48         Arrays.fill(out1, (byte)0);
49         Arrays.fill(out2, (byte)0);
50 
51         try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
52             try (DeflaterOutputStream defos = new DeflaterOutputStream(baos, def)) {
53                 defos.write(in, 0, len);
54             }
55             out1 = baos.toByteArray();
56         }
57         int m = out1.length;
58 
59         Inflater inf = new Inflater(nowrap);
60         inf.setInput(out1, 0, m);
61         int n = inf.inflate(out2);
62 
63         if (n != len ||
64             !Arrays.equals(Arrays.copyOf(in, len), Arrays.copyOf(out2, len)) ||
65             inf.inflate(out2) != 0) {
66             System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
67                               m, n, len, Arrays.equals(in, out2));
68             throw new RuntimeException("De/inflater failed:" + def);
69         }
70     }
71 
checkByteBuffer(Deflater def, Inflater inf, ByteBuffer in, ByteBuffer out1, ByteBuffer out2, byte[] expected, int len, byte[] result, boolean out1ReadOnlyWhenInflate)72     static void checkByteBuffer(Deflater def, Inflater inf,
73                                 ByteBuffer in, ByteBuffer out1, ByteBuffer out2,
74                                 byte[] expected, int len, byte[] result,
75                                 boolean out1ReadOnlyWhenInflate)
76             throws Throwable {
77         def.reset();
78         inf.reset();
79 
80         def.setInput(in);
81         def.finish();
82         int m = def.deflate(out1);
83 
84         out1.flip();
85         if (out1ReadOnlyWhenInflate)
86             out1 = out1.asReadOnlyBuffer();
87         inf.setInput(out1);
88         int n = inf.inflate(out2);
89 
90         out2.flip();
91         out2.get(result, 0, n);
92 
93         if (n != len || out2.position() != len ||
94             !Arrays.equals(Arrays.copyOf(expected, len), Arrays.copyOf(result, len)) ||
95             inf.inflate(result) != 0) {
96             throw new RuntimeException("De/inflater(buffer) failed:" + def);
97         }
98     }
99 
checkByteBufferReadonly(Deflater def, Inflater inf, ByteBuffer in, ByteBuffer out1, ByteBuffer out2)100     static void checkByteBufferReadonly(Deflater def, Inflater inf,
101                                         ByteBuffer in, ByteBuffer out1, ByteBuffer out2)
102             throws Throwable {
103         def.reset();
104         inf.reset();
105         def.setInput(in);
106         def.finish();
107         int m = -1;
108         if (!out2.isReadOnly())
109             out2 = out2.asReadOnlyBuffer();
110         try {
111             m = def.deflate(out2);
112             throw new RuntimeException("deflater: ReadOnlyBufferException: failed");
113         } catch (ReadOnlyBufferException robe) {}
114         m = def.deflate(out1);
115         out1.flip();
116         inf.setInput(out1);
117         try {
118             inf.inflate(out2);
119             throw new RuntimeException("inflater: ReadOnlyBufferException: failed");
120         } catch (ReadOnlyBufferException robe) {}
121     }
122 
123     /**
124      * Uses the {@code def} deflater to deflate the input data {@code in} of length {@code len}.
125      * A new {@link Inflater} is then created within this method to inflate the deflated data. The
126      * inflated data is then compared with the {@code in} to assert that it matches the original
127      * input data.
128      * This method repeats these checks for the different overloaded methods of
129      * {@code Deflater.deflate(...)} and {@code Inflater.inflate(...)}
130      *
131      * @param def    the deflater to use for deflating the contents in {@code in}
132      * @param in     the input content
133      * @param len    the length of the input content to use
134      * @param nowrap will be passed to the constructor of the {@code Inflater} used in this
135      *               method
136      * @throws Throwable if any error occurs during the check
137      */
check(Deflater def, byte[] in, int len, boolean nowrap)138     static void check(Deflater def, byte[] in, int len, boolean nowrap)
139         throws Throwable
140     {
141         byte[] tempBuffer = new byte[1024];
142         byte[] out1, out2;
143         int m = 0, n = 0;
144         Inflater inf = new Inflater(nowrap);
145         def.setInput(in, 0, len);
146         def.finish();
147 
148         try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
149             while (!def.finished()) {
150                 int temp_counter = def.deflate(tempBuffer);
151                 m += temp_counter;
152                 baos.write(tempBuffer, 0, temp_counter);
153             }
154             out1 = baos.toByteArray();
155             baos.reset();
156 
157             inf.setInput(out1, 0, m);
158 
159             while (!inf.finished()) {
160                 int temp_counter = inf.inflate(tempBuffer);
161                 n += temp_counter;
162                 baos.write(tempBuffer, 0, temp_counter);
163             }
164             out2 = baos.toByteArray();
165             if (n != len ||
166                 !Arrays.equals(in, 0, len, out2, 0, len) ||
167                 inf.inflate(out2) != 0) {
168                 System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
169                                   m, n, len, Arrays.equals(in, out2));
170                 throw new RuntimeException("De/inflater failed:" + def);
171             }
172         }
173 
174         // readable
175         Arrays.fill(out1, (byte)0);
176         Arrays.fill(out2, (byte)0);
177         ByteBuffer bbIn = ByteBuffer.wrap(in, 0, len);
178         ByteBuffer bbOut1 = ByteBuffer.wrap(out1);
179         ByteBuffer bbOut2 = ByteBuffer.wrap(out2);
180         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
181         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
182 
183         // readonly in
184         Arrays.fill(out1, (byte)0);
185         Arrays.fill(out2, (byte)0);
186         bbIn = ByteBuffer.wrap(in, 0, len).asReadOnlyBuffer();
187         bbOut1 = ByteBuffer.wrap(out1);
188         bbOut2 = ByteBuffer.wrap(out2);
189         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
190         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
191 
192         // readonly out1 when inflate
193         Arrays.fill(out1, (byte)0);
194         Arrays.fill(out2, (byte)0);
195         bbIn = ByteBuffer.wrap(in, 0, len);
196         bbOut1 = ByteBuffer.wrap(out1);
197         bbOut2 = ByteBuffer.wrap(out2);
198         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, true);
199         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
200 
201         // direct
202         bbIn = ByteBuffer.allocateDirect(in.length);
203         bbIn.put(in, 0, n).flip();
204         bbOut1 = ByteBuffer.allocateDirect(out1.length);
205         bbOut2 = ByteBuffer.allocateDirect(out2.length);
206         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
207         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
208     }
209 
checkDict(Deflater def, Inflater inf, byte[] src, byte[] dstDef, byte[] dstInf, ByteBuffer dictDef, ByteBuffer dictInf)210     static void checkDict(Deflater def, Inflater inf, byte[] src,
211                           byte[] dstDef, byte[] dstInf,
212                           ByteBuffer dictDef,  ByteBuffer dictInf) throws Throwable {
213         def.reset();
214         inf.reset();
215 
216         def.setDictionary(dictDef);
217         def.setInput(src);
218         def.finish();
219         int n = def.deflate(dstDef);
220 
221         inf.setInput(dstDef, 0, n);
222         n = inf.inflate(dstInf);
223         if (n != 0 || !inf.needsDictionary()) {
224             throw new RuntimeException("checkDict failed: need dict to continue");
225         }
226         inf.setDictionary(dictInf);
227         n = inf.inflate(dstInf);
228         // System.out.println("result: " + new String(dstInf, 0, n));
229         if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
230             throw new RuntimeException("checkDict failed: inflate result");
231         }
232     }
233 
checkDict(int level, int strategy)234     static void checkDict(int level, int strategy) throws Throwable {
235 
236         Deflater def = newDeflater(level, strategy, false, new byte[0]);
237         Inflater inf = new Inflater();
238 
239         byte[] src = "hello world, hello world, hello sherman".getBytes();
240         byte[] dict = "hello".getBytes();
241 
242         byte[] dstDef = new byte[1024];
243         byte[] dstInf = new byte[1024];
244 
245         def.setDictionary(dict);
246         def.setInput(src);
247         def.finish();
248         int n = def.deflate(dstDef);
249 
250         inf.setInput(dstDef, 0, n);
251         n = inf.inflate(dstInf);
252         if (n != 0 || !inf.needsDictionary()) {
253             throw new RuntimeException("checkDict failed: need dict to continue");
254         }
255         inf.setDictionary(dict);
256         n = inf.inflate(dstInf);
257         //System.out.println("result: " + new String(dstInf, 0, n));
258         if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
259             throw new RuntimeException("checkDict failed: inflate result");
260         }
261 
262         ByteBuffer dictDef = ByteBuffer.wrap(dict);
263         ByteBuffer dictInf = ByteBuffer.wrap(dict);
264         checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
265 
266         dictDef = ByteBuffer.allocateDirect(dict.length);
267         dictInf = ByteBuffer.allocateDirect(dict.length);
268         dictDef.put(dict).flip();
269         dictInf.put(dict).flip();
270         checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
271 
272         def.end();
273         inf.end();
274     }
275 
newDeflater(int level, int strategy, boolean dowrap, byte[] tmp)276     private static Deflater newDeflater(int level, int strategy, boolean dowrap, byte[] tmp) {
277         Deflater def = new Deflater(level, dowrap);
278         if (strategy != Deflater.DEFAULT_STRATEGY) {
279             def.setStrategy(strategy);
280             // The first invocation after setLevel/Strategy()
281             // with a different level/stragety returns 0, if
282             // there is no need to flush out anything for the
283             // previous setting/"data", this is tricky and
284             // appears un-documented.
285             def.deflate(tmp);
286         }
287         return def;
288     }
289 
resetDeflater(Deflater def, int level, int strategy)290     private static Deflater resetDeflater(Deflater def, int level, int strategy) {
291         def.setLevel(level);
292         def.setStrategy(strategy);
293         def.reset();
294         return def;
295     }
296 
main(String[] args)297     public static void main(String[] args) throws Throwable {
298 
299         byte[] dataIn = new byte[1024 * 512];
300         rnd.nextBytes(dataIn);
301         byte[] dataOut1 = new byte[dataIn.length + 1024];
302         byte[] dataOut2 = new byte[dataIn.length];
303 
304         Deflater defNotWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
305         Deflater defWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
306 
307         for (int level = Deflater.DEFAULT_COMPRESSION;
308                  level <= Deflater.BEST_COMPRESSION; level++) {
309             for (int strategy = Deflater.DEFAULT_STRATEGY;
310                      strategy <= Deflater.HUFFMAN_ONLY; strategy++) {
311                 for (boolean dowrap : new boolean[] { false, true }) {
312                     System.out.println("level:" + level +
313                                      ", strategy: " + strategy +
314                                      ", dowrap: " + dowrap);
315                     for (int i = 0; i < 5; i++) {
316                         int len = (i == 0)? dataIn.length
317                                           : new Random().nextInt(dataIn.length);
318                         System.out.println("iteration: " + (i + 1) + " input length: " + len);
319                         // use a new deflater
320                         Deflater def = newDeflater(level, strategy, dowrap, dataOut2);
321                         check(def, dataIn, len, dowrap);
322                         def.end();
323 
324                         // reuse the deflater (with reset) and test on stream, which
325                         // uses a "smaller" buffer (smaller than the overall data)
326                         def = resetDeflater(dowrap ? defWrap : defNotWrap, level, strategy);
327                         checkStream(def, dataIn, len, dataOut1, dataOut2, dowrap);
328                     }
329                 }
330                 // test setDictionary()
331                 checkDict(level, strategy);
332             }
333         }
334     }
335 }
336