1 /*
2  * Copyright (c) 2011, 2018, 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 import java.io.*;
32 import java.nio.*;
33 import java.util.*;
34 import java.util.zip.*;
35 
36 import static java.nio.charset.StandardCharsets.UTF_8;
37 
38 public class DeInflate {
39 
40     private static Random rnd = new Random();
41 
42 
checkStream(Deflater def, byte[] in, int len, byte[] out1, byte[] out2, boolean nowrap)43     static void checkStream(Deflater def, byte[] in, int len,
44                             byte[] out1, byte[] out2, boolean nowrap)
45         throws Throwable
46     {
47         Arrays.fill(out1, (byte)0);
48         Arrays.fill(out2, (byte)0);
49 
50         ByteArrayOutputStream baos = new ByteArrayOutputStream();
51         try (DeflaterOutputStream defos = new DeflaterOutputStream(baos, def)) {
52             defos.write(in, 0, len);
53         }
54         out1 = baos.toByteArray();
55         int m = out1.length;
56 
57         Inflater inf = new Inflater(nowrap);
58         inf.setInput(out1, 0, m);
59         int n = inf.inflate(out2);
60 
61         if (n != len ||
62             !Arrays.equals(Arrays.copyOf(in, len), Arrays.copyOf(out2, len)) ||
63             inf.inflate(out2) != 0) {
64             System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
65                               m, n, len, Arrays.equals(in, out2));
66             throw new RuntimeException("De/inflater failed:" + def);
67         }
68     }
69 
checkByteBuffer(Deflater def, Inflater inf, ByteBuffer in, ByteBuffer out1, ByteBuffer out2, byte[] expected, int len, byte[] result, boolean out1ReadOnlyWhenInflate)70     static void checkByteBuffer(Deflater def, Inflater inf,
71                                 ByteBuffer in, ByteBuffer out1, ByteBuffer out2,
72                                 byte[] expected, int len, byte[] result,
73                                 boolean out1ReadOnlyWhenInflate)
74             throws Throwable {
75         def.reset();
76         inf.reset();
77 
78         def.setInput(in);
79         def.finish();
80         int m = def.deflate(out1);
81 
82         out1.flip();
83         if (out1ReadOnlyWhenInflate)
84             out1 = out1.asReadOnlyBuffer();
85         inf.setInput(out1);
86         int n = inf.inflate(out2);
87 
88         out2.flip();
89         out2.get(result, 0, n);
90 
91         if (n != len || out2.position() != len ||
92             !Arrays.equals(Arrays.copyOf(expected, len), Arrays.copyOf(result, len)) ||
93             inf.inflate(result) != 0) {
94             throw new RuntimeException("De/inflater(buffer) failed:" + def);
95         }
96     }
97 
checkByteBufferReadonly(Deflater def, Inflater inf, ByteBuffer in, ByteBuffer out1, ByteBuffer out2)98     static void checkByteBufferReadonly(Deflater def, Inflater inf,
99                                         ByteBuffer in, ByteBuffer out1, ByteBuffer out2)
100             throws Throwable {
101         def.reset();
102         inf.reset();
103         def.setInput(in);
104         def.finish();
105         int m = -1;
106         if (!out2.isReadOnly())
107             out2 = out2.asReadOnlyBuffer();
108         try {
109             m = def.deflate(out2);
110             throw new RuntimeException("deflater: ReadOnlyBufferException: failed");
111         } catch (ReadOnlyBufferException robe) {}
112         m = def.deflate(out1);
113         out1.flip();
114         inf.setInput(out1);
115         try {
116             inf.inflate(out2);
117             throw new RuntimeException("inflater: ReadOnlyBufferException: failed");
118         } catch (ReadOnlyBufferException robe) {}
119     }
120 
check(Deflater def, byte[] in, int len, byte[] out1, byte[] out2, boolean nowrap)121     static void check(Deflater def, byte[] in, int len,
122                       byte[] out1, byte[] out2, boolean nowrap)
123         throws Throwable
124     {
125         Arrays.fill(out1, (byte)0);
126         Arrays.fill(out2, (byte)0);
127 
128         def.setInput(in, 0, len);
129         def.finish();
130         int m = def.deflate(out1);
131 
132         Inflater inf = new Inflater(nowrap);
133         inf.setInput(out1, 0, m);
134         int n = inf.inflate(out2);
135 
136         if (n != len ||
137             !Arrays.equals(Arrays.copyOf(in, len), Arrays.copyOf(out2, len)) ||
138             inf.inflate(out2) != 0) {
139             System.out.printf("m=%d, n=%d, len=%d, eq=%b%n",
140                               m, n, len, Arrays.equals(in, out2));
141             throw new RuntimeException("De/inflater failed:" + def);
142         }
143 
144         // readable
145         Arrays.fill(out1, (byte)0);
146         Arrays.fill(out2, (byte)0);
147         ByteBuffer bbIn = ByteBuffer.wrap(in, 0, len);
148         ByteBuffer bbOut1 = ByteBuffer.wrap(out1);
149         ByteBuffer bbOut2 = ByteBuffer.wrap(out2);
150         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
151         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
152 
153         // readonly in
154         Arrays.fill(out1, (byte)0);
155         Arrays.fill(out2, (byte)0);
156         bbIn = ByteBuffer.wrap(in, 0, len).asReadOnlyBuffer();
157         bbOut1 = ByteBuffer.wrap(out1);
158         bbOut2 = ByteBuffer.wrap(out2);
159         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
160         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
161 
162         // readonly out1 when inflate
163         Arrays.fill(out1, (byte)0);
164         Arrays.fill(out2, (byte)0);
165         bbIn = ByteBuffer.wrap(in, 0, len);
166         bbOut1 = ByteBuffer.wrap(out1);
167         bbOut2 = ByteBuffer.wrap(out2);
168         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, true);
169         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
170 
171         // direct
172         bbIn = ByteBuffer.allocateDirect(in.length);
173         bbIn.put(in, 0, n).flip();
174         bbOut1 = ByteBuffer.allocateDirect(out1.length);
175         bbOut2 = ByteBuffer.allocateDirect(out2.length);
176         checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false);
177         checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2);
178     }
179 
checkDict(Deflater def, Inflater inf, byte[] src, byte[] dstDef, byte[] dstInf, ByteBuffer dictDef, ByteBuffer dictInf)180     static void checkDict(Deflater def, Inflater inf, byte[] src,
181                           byte[] dstDef, byte[] dstInf,
182                           ByteBuffer dictDef,  ByteBuffer dictInf) throws Throwable {
183         def.reset();
184         inf.reset();
185 
186         def.setDictionary(dictDef);
187         def.setInput(src);
188         def.finish();
189         int n = def.deflate(dstDef);
190 
191         inf.setInput(dstDef, 0, n);
192         n = inf.inflate(dstInf);
193         if (n != 0 || !inf.needsDictionary()) {
194             throw new RuntimeException("checkDict failed: need dict to continue");
195         }
196         inf.setDictionary(dictInf);
197         n = inf.inflate(dstInf);
198         // System.out.println("result: " + new String(dstInf, 0, n));
199         if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
200             throw new RuntimeException("checkDict failed: inflate result");
201         }
202     }
203 
checkDict(int level, int strategy)204     static void checkDict(int level, int strategy) throws Throwable {
205 
206         Deflater def = newDeflater(level, strategy, false, new byte[0]);
207         Inflater inf = new Inflater();
208 
209         byte[] src = "hello world, hello world, hello sherman".getBytes();
210         byte[] dict = "hello".getBytes();
211 
212         byte[] dstDef = new byte[1024];
213         byte[] dstInf = new byte[1024];
214 
215         def.setDictionary(dict);
216         def.setInput(src);
217         def.finish();
218         int n = def.deflate(dstDef);
219 
220         inf.setInput(dstDef, 0, n);
221         n = inf.inflate(dstInf);
222         if (n != 0 || !inf.needsDictionary()) {
223             throw new RuntimeException("checkDict failed: need dict to continue");
224         }
225         inf.setDictionary(dict);
226         n = inf.inflate(dstInf);
227         //System.out.println("result: " + new String(dstInf, 0, n));
228         if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) {
229             throw new RuntimeException("checkDict failed: inflate result");
230         }
231 
232         ByteBuffer dictDef = ByteBuffer.wrap(dict);
233         ByteBuffer dictInf = ByteBuffer.wrap(dict);
234         checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
235 
236         dictDef = ByteBuffer.allocateDirect(dict.length);
237         dictInf = ByteBuffer.allocateDirect(dict.length);
238         dictDef.put(dict).flip();
239         dictInf.put(dict).flip();
240         checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf);
241 
242         def.end();
243         inf.end();
244     }
245 
newDeflater(int level, int strategy, boolean dowrap, byte[] tmp)246     private static Deflater newDeflater(int level, int strategy, boolean dowrap, byte[] tmp) {
247         Deflater def = new Deflater(level, dowrap);
248         if (strategy != Deflater.DEFAULT_STRATEGY) {
249             def.setStrategy(strategy);
250             // The first invocation after setLevel/Strategy()
251             // with a different level/stragety returns 0, if
252             // there is no need to flush out anything for the
253             // previous setting/"data", this is tricky and
254             // appears un-documented.
255             def.deflate(tmp);
256         }
257         return def;
258     }
259 
resetDeflater(Deflater def, int level, int strategy)260     private static Deflater resetDeflater(Deflater def, int level, int strategy) {
261         def.setLevel(level);
262         def.setStrategy(strategy);
263         def.reset();
264         return def;
265     }
266 
main(String[] args)267     public static void main(String[] args) throws Throwable {
268 
269         byte[] dataIn = new byte[1024 * 512];
270         rnd.nextBytes(dataIn);
271         byte[] dataOut1 = new byte[dataIn.length + 1024];
272         byte[] dataOut2 = new byte[dataIn.length];
273 
274         Deflater defNotWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, false);
275         Deflater defWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
276 
277         for (int level = Deflater.DEFAULT_COMPRESSION;
278                  level <= Deflater.BEST_COMPRESSION; level++) {
279             for (int strategy = Deflater.DEFAULT_STRATEGY;
280                      strategy <= Deflater.HUFFMAN_ONLY; strategy++) {
281                 for (boolean dowrap : new boolean[] { false, true }) {
282                     System.out.println("level:" + level +
283                                      ", strategy: " + strategy +
284                                      ", dowrap: " + dowrap);
285                     for (int i = 0; i < 5; i++) {
286                         int len = (i == 0)? dataIn.length
287                                           : new Random().nextInt(dataIn.length);
288                         // use a new deflater
289                         Deflater def = newDeflater(level, strategy, dowrap, dataOut2);
290                         check(def, dataIn, len, dataOut1, dataOut2, dowrap);
291                         def.end();
292 
293                         // reuse the deflater (with reset) and test on stream, which
294                         // uses a "smaller" buffer (smaller than the overall data)
295                         def = resetDeflater(dowrap ? defWrap : defNotWrap, level, strategy);
296                         checkStream(def, dataIn, len, dataOut1, dataOut2, dowrap);
297                     }
298                 }
299                 // test setDictionary()
300                 checkDict(level, strategy);
301             }
302         }
303     }
304 }
305