GZIPOutputStream.java revision 51b1b6997fd3f980076b8081f7f1165ccc2a4008
1/*
2 * Copyright (c) 1996, 2010, 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
26package java.util.zip;
27
28import java.io.OutputStream;
29import 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 *
36 */
37public
38class GZIPOutputStream extends DeflaterOutputStream {
39    /**
40     * CRC-32 of uncompressed data.
41     */
42    protected CRC32 crc = new CRC32();
43
44    /*
45     * GZIP header magic number.
46     */
47    private final static int GZIP_MAGIC = 0x8b1f;
48
49    /*
50     * Trailer size in bytes.
51     *
52     */
53    private final static int TRAILER_SIZE = 8;
54
55    /**
56     * Creates a new output stream with the specified buffer size.
57     *
58     * <p>The new output stream instance is created as if by invoking
59     * the 3-argument constructor GZIPOutputStream(out, size, false).
60     *
61     * @param out the output stream
62     * @param size the output buffer size
63     * @exception IOException If an I/O error has occurred.
64     * @exception IllegalArgumentException if size is <= 0
65
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 size is <= 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