1 /*
2  * Copyright (c) 2011, 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  * ZIP inflater/deflater performance.
26  */
27 
28 /*
29  * Run this test on JDK 6 and on later
30  * JDKs to compare results:
31  *     java -server -Xms1024M -Xmx1024M -Ddebug=true FlaterCriticalArray
32  *
33  * The performance issues can be readily seen on JDK 6, and so this code is
34  * written to compile on that platform: it does *not* use any JDK 7 - specific
35  * features.
36  */
37 
38 import java.io.*;
39 import java.nio.*;
40 import java.util.*;
41 import java.util.zip.*;
42 
43 public class FlaterCriticalArray {
44     // If true, print information about performance
45     private static final boolean debug = System.getProperty("debug") != null;
46 
debug(String s)47     private static void debug(String s) {
48         if (debug) System.out.println(s);
49     }
50 
debug(String name, String inOut, long time, int length)51     private static void debug(String name, String inOut, long time, int length) {
52         debug(name + ": Duration of " + inOut + "(in ms): " + time);
53         debug(name + ": " + inOut + "d data length: " + length + " bytes");
54     }
55 
grow(byte[] a, int capacity)56     private static byte[] grow(byte[] a, int capacity) {
57         while (a.length < capacity) {
58             byte[] a2 = new byte[a.length * 2];
59             System.arraycopy(a, 0, a2, 0, a.length);
60             a = a2;
61         }
62         return a;
63     }
64 
trim(byte[] a, int length)65     private static byte[] trim(byte[] a, int length) {
66         byte[] res = new byte[length];
67         System.arraycopy(a, 0, res, 0, length);
68         return res;
69     }
70 
71     /*
72      * Base class for individual test cases
73      */
74     private abstract static class TestCase {
75         protected String name;  // For information in debug messages
76         protected byte data[];  // Data to be deflated and subsequently inflated
77         protected int level;    // Compression level for deflater
78 
TestCase(String name, byte data[])79         protected TestCase(String name, byte data[]) {
80             this(name, data, -1);
81         }
82 
TestCase(String name, byte data[], int level)83         protected TestCase(String name, byte data[], int level) {
84             this.name = name;
85             this.data = data;
86             this.level = level;
87         }
88 
runTest()89         public void runTest() throws Throwable {
90             long time0, time1;
91             byte deflated[], inflated[];
92 
93             debug("");
94 
95             time0 = System.currentTimeMillis();
96             deflated = deflate(data, level);
97             time1 = System.currentTimeMillis();
98             inform("Deflate", time1 - time0, deflated.length);
99 
100             time0 = System.currentTimeMillis();
101             inflated = inflate(deflated);
102             time1 = System.currentTimeMillis();
103             inform("Inflate", time1 - time0, inflated.length);
104 
105             check(Arrays.equals(data, inflated),
106                   name + ": Inflated and deflated arrays do not match");
107         }
108 
inform(String inOut, long duration, int length)109         private void inform(String inOut, long duration, int length) {
110             debug(name, inOut, duration, length);
111         }
112 
deflate(byte data[], int level)113         protected abstract byte[] deflate(byte data[], int level) throws Throwable;
114 
inflate(byte deflated[])115         protected abstract byte[] inflate(byte deflated[]) throws Throwable;
116     }
117 
118     /*
119      * Following are  the individual test cases
120      */
121 
122     private static class StrideTest extends TestCase {
123         static final int STRIDE = 1024;
124 
StrideTest(byte data[], int level)125         public StrideTest(byte data[], int level) {
126             super("STRIDE", data, level);
127         }
128 
deflate(byte in[], int level)129         protected byte[] deflate(byte in[], int level) throws Throwable {
130             final int len = in.length;
131             final Deflater deflater = new Deflater(level);
132             final byte[] smallBuffer = new byte[32];
133             byte[] flated = new byte[32];
134             int count = 0;
135             for (int i = 0; i<len; i+= STRIDE) {
136                 deflater.setInput(in, i, Math.min(STRIDE, len-i));
137                 while (!deflater.needsInput()) {
138                     int n = deflater.deflate(smallBuffer);
139                     flated = grow(flated, count + n);
140                     System.arraycopy(smallBuffer, 0, flated, count, n);
141                     count += n;
142                 }
143             }
144             deflater.finish();
145             int n;
146             do {
147                 n = deflater.deflate(smallBuffer);
148                 flated = grow(flated, count + n);
149                 System.arraycopy(smallBuffer, 0, flated, count, n);
150                 count += n;
151             } while (n > 0);
152             return trim(flated, count);
153         }
154 
inflate(byte in[])155         protected byte[] inflate(byte in[]) throws Throwable {
156             final int len = in.length;
157             final Inflater inflater = new Inflater();
158             final byte[] smallBuffer = new byte[3200];
159 
160             byte[] flated = new byte[32];
161             int count = 0;
162 
163             for (int i = 0; i<len; i+= STRIDE) {
164                 inflater.setInput(in, i, Math.min(STRIDE, len-i));
165                 while (!inflater.needsInput()) {
166                     int n;
167                     while ((n = inflater.inflate(smallBuffer)) > 0) {
168                         flated = grow(flated, count + n);
169                         System.arraycopy(smallBuffer, 0, flated, count, n);
170                         count += n;
171                     }
172                 }
173             }
174             return trim(flated, count);
175         }
176     }
177 
178     private static class NoStrideTest extends TestCase {
NoStrideTest(byte data[], int level)179         public NoStrideTest(byte data[], int level) {
180             super("NO STRIDE", data, level);
181         }
182 
deflate(byte in[], int level)183         public byte[] deflate(byte in[], int level) throws Throwable {
184             final Deflater flater = new Deflater(level);
185             flater.setInput(in);
186             flater.finish();
187             final byte[] smallBuffer = new byte[32];
188             byte[] flated = new byte[32];
189             int count = 0;
190             int n;
191             while ((n = flater.deflate(smallBuffer)) > 0) {
192                 flated = grow(flated, count + n);
193                 System.arraycopy(smallBuffer, 0, flated, count, n);
194                 count += n;
195             }
196             return trim(flated, count);
197         }
198 
inflate(byte in[])199         public byte[] inflate(byte in[]) throws Throwable {
200             final Inflater flater = new Inflater();
201             flater.setInput(in);
202             final byte[] smallBuffer = new byte[32];
203             byte[] flated = new byte[32];
204             int count = 0;
205             int n;
206             while ((n = flater.inflate(smallBuffer)) > 0) {
207                 flated = grow(flated, count + n);
208                 System.arraycopy(smallBuffer, 0, flated, count, n);
209                 count += n;
210             }
211             return trim(flated, count);
212         }
213     }
214 
215     /**
216      * Check Deflater{In,Out}putStream by way of GZIP{In,Out}putStream
217      */
218     private static class GZIPTest extends TestCase {
GZIPTest(byte data[])219         public GZIPTest(byte data[]) {
220             super("GZIP", data);
221         }
222 
deflate(byte data[], int ignored)223         public byte[] deflate(byte data[], int ignored) throws Throwable {
224             ByteArrayOutputStream baos = new ByteArrayOutputStream();
225             OutputStream gzos = new GZIPOutputStream(baos);
226             gzos.write(data, 0, data.length);
227             gzos.close();
228             return baos.toByteArray();
229         }
230 
inflate(byte deflated[])231         public byte[] inflate(byte deflated[]) throws Throwable {
232             InputStream bais = new ByteArrayInputStream(deflated);
233             GZIPInputStream gzis = new GZIPInputStream(bais);
234             byte[] inflated = new byte[data.length];
235             int numRead = 0;
236             int count = 0;
237             while ((numRead = gzis.read(inflated, count, data.length - count)) > 0) {
238                 count += numRead;
239             }
240             check(count == data.length, name + ": Read " + count + "; expected " + data.length);
241             return inflated;
242         }
243     }
244 
realMain(String[] args)245     public static void realMain(String[] args) throws Throwable {
246         byte data[];
247         int level = -1;
248         if (args.length > 0) {
249           level = Integer.parseInt(args[0]);
250         }
251         debug("Using level " + level);
252 
253         if (args.length > 1) {
254             FileInputStream fis = new FileInputStream(args[1]);
255             int len = fis.available();
256             data = new byte[len];
257             check(fis.read(data, 0, len) == len, "Did not read complete file");
258             debug("Original data from " + args[1]);
259             fis.close();
260         } else {
261             ByteBuffer bb = ByteBuffer.allocate(8);
262             ByteArrayOutputStream baos = new ByteArrayOutputStream();
263             for (int i = 0; i < 1024 * 64; i++) { // data length
264                 bb.putDouble(0, Math.random());
265                 baos.write(bb.array(), 0, 8);
266             }
267             data = baos.toByteArray();
268             debug("Original data from random byte array");
269         }
270         debug("Original data length: " + data.length + " bytes");
271 
272         new StrideTest(data, level).runTest();
273         new NoStrideTest(data, level).runTest();
274         new GZIPTest(data).runTest();
275     }
276 
277     //--------------------- Infrastructure ---------------------------
278     static volatile int passed = 0, failed = 0;
pass()279     static void pass() {passed++;}
pass(String msg)280     static void pass(String msg) {System.out.println(msg); passed++;}
fail()281     static void fail() {failed++; Thread.dumpStack();}
fail(String msg)282     static void fail(String msg) {System.out.println(msg); fail();}
unexpected(Throwable t)283     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
unexpected(Throwable t, String msg)284     static void unexpected(Throwable t, String msg) {
285         System.out.println(msg); failed++; t.printStackTrace();}
check(boolean cond)286     static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
check(boolean cond, String msg)287     static boolean check(boolean cond, String msg) {if (cond) pass(); else fail(msg); return cond;}
equal(Object x, Object y)288     static void equal(Object x, Object y) {
289         if (x == null ? y == null : x.equals(y)) pass();
290         else fail(x + " not equal to " + y);}
main(String[] args)291     public static void main(String[] args) throws Throwable {
292         try {realMain(args);} catch (Throwable t) {unexpected(t);}
293         System.out.println("\nPassed = " + passed + " failed = " + failed);
294         if (failed > 0) throw new AssertionError("Some tests failed");}
295 }
296