1 /*
2 * Copyright (c) 1996, 2013, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util.zip;
27
28 import java.io.OutputStream;
29 import java.io.IOException;
30
31 /**
32 * This class implements a stream filter for writing compressed data in
33 * the GZIP file format.
34 * @author David Connelly
35 * @since 1.1
36 *
37 */
38 public
39 class GZIPOutputStream extends DeflaterOutputStream {
40 /**
41 * CRC-32 of uncompressed data.
42 */
43 protected CRC32 crc = new CRC32();
44
45 /*
46 * GZIP header magic number.
47 */
48 private static final int GZIP_MAGIC = 0x8b1f;
49
50 /*
51 * Trailer size in bytes.
52 *
53 */
54 private static final int TRAILER_SIZE = 8;
55
56 /**
57 * Creates a new output stream with the specified buffer size.
58 *
59 * <p>The new output stream instance is created as if by invoking
60 * the 3-argument constructor GZIPOutputStream(out, size, false).
61 *
62 * @param out the output stream
63 * @param size the output buffer size
64 * @exception IOException If an I/O error has occurred.
65 * @exception IllegalArgumentException if {@code size <= 0}
66 */
67 public GZIPOutputStream(OutputStream out, int size) throws IOException {
68 this(out, size, false);
69 }
70
71 /**
72 * Creates a new output stream with the specified buffer size and
73 * flush mode.
74 *
75 * @param out the output stream
76 * @param size the output buffer size
77 * @param syncFlush
78 * if {@code true} invocation of the inherited
79 * {@link DeflaterOutputStream#flush() flush()} method of
80 * this instance flushes the compressor with flush mode
81 * {@link Deflater#SYNC_FLUSH} before flushing the output
82 * stream, otherwise only flushes the output stream
83 * @exception IOException If an I/O error has occurred.
84 * @exception IllegalArgumentException if {@code size <= 0}
85 *
86 * @since 1.7
87 */
88 public GZIPOutputStream(OutputStream out, int size, boolean syncFlush)
89 throws IOException
90 {
91 super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true),
92 size,
93 syncFlush);
94 usesDefaultDeflater = true;
95 writeHeader();
96 crc.reset();
97 }
98
99
100 /**
101 * Creates a new output stream with a default buffer size.
102 *
103 * <p>The new output stream instance is created as if by invoking
104 * the 2-argument constructor GZIPOutputStream(out, false).
105 *
106 * @param out the output stream
107 * @exception IOException If an I/O error has occurred.
108 */
109 public GZIPOutputStream(OutputStream out) throws IOException {
110 this(out, 512, false);
111 }
112
113 /**
114 * Creates a new output stream with a default buffer size and
115 * the specified flush mode.
116 *
117 * @param out the output stream
118 * @param syncFlush
119 * if {@code true} invocation of the inherited
120 * {@link DeflaterOutputStream#flush() flush()} method of
121 * this instance flushes the compressor with flush mode
122 * {@link Deflater#SYNC_FLUSH} before flushing the output
123 * stream, otherwise only flushes the output stream
124 *
125 * @exception IOException If an I/O error has occurred.
126 *
127 * @since 1.7
128 */
129 public GZIPOutputStream(OutputStream out, boolean syncFlush)
130 throws IOException
131 {
132 this(out, 512, syncFlush);
133 }
134
135 /**
136 * Writes array of bytes to the compressed output stream. This method
137 * will block until all the bytes are written.
138 * @param buf the data to be written
139 * @param off the start offset of the data
140 * @param len the length of the data
141 * @exception IOException If an I/O error has occurred.
142 */
143 public synchronized void write(byte[] buf, int off, int len)
144 throws IOException
145 {
146 super.write(buf, off, len);
147 crc.update(buf, off, len);
148 }
149
150 /**
151 * Finishes writing compressed data to the output stream without closing
152 * the underlying stream. Use this method when applying multiple filters
153 * in succession to the same output stream.
154 * @exception IOException if an I/O error has occurred
155 */
156 public void finish() throws IOException {
157 if (!def.finished()) {
158 def.finish();
159 while (!def.finished()) {
160 int len = def.deflate(buf, 0, buf.length);
161 if (def.finished() && len <= buf.length - TRAILER_SIZE) {
162 // last deflater buffer. Fit trailer at the end
163 writeTrailer(buf, len);
164 len = len + TRAILER_SIZE;
165 out.write(buf, 0, len);
166 return;
167 }
168 if (len > 0)
169 out.write(buf, 0, len);
170 }
171 // if we can't fit the trailer at the end of the last
172 // deflater buffer, we write it separately
173 byte[] trailer = new byte[TRAILER_SIZE];
174 writeTrailer(trailer, 0);
175 out.write(trailer);
176 }
177 }
178
179 /*
180 * Writes GZIP member header.
181 */
182 private void writeHeader() throws IOException {
183 out.write(new byte[] {
184 (byte) GZIP_MAGIC, // Magic number (short)
185 (byte)(GZIP_MAGIC >> 8), // Magic number (short)
186 Deflater.DEFLATED, // Compression method (CM)
187 0, // Flags (FLG)
188 0, // Modification time MTIME (int)
189 0, // Modification time MTIME (int)
190 0, // Modification time MTIME (int)
191 0, // Modification time MTIME (int)
192 0, // Extra flags (XFLG)
193 0 // Operating system (OS)
194 });
195 }
196
197 /*
198 * Writes GZIP member trailer to a byte array, starting at a given
199 * offset.
200 */
201 private void writeTrailer(byte[] buf, int offset) throws IOException {
202 writeInt((int)crc.getValue(), buf, offset); // CRC-32 of uncompr. data
203 writeInt(def.getTotalIn(), buf, offset + 4); // Number of uncompr. bytes
204 }
205
206 /*
207 * Writes integer in Intel byte order to a byte array, starting at a
208 * given offset.
209 */
210 private void writeInt(int i, byte[] buf, int offset) throws IOException {
211 writeShort(i & 0xffff, buf, offset);
212 writeShort((i >> 16) & 0xffff, buf, offset + 2);
213 }
214
215 /*
216 * Writes short integer in Intel byte order to a byte array, starting
217 * at a given offset
218 */
219 private void writeShort(int s, byte[] buf, int offset) throws IOException {
220 buf[offset] = (byte)(s & 0xff);
221 buf[offset + 1] = (byte)((s >> 8) & 0xff);
222 }
223 }
224