DeflaterOutputStream.java revision 49965c1dc9da104344f4893a05e45795a5740d20
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package java.util.zip;
28
29import java.io.FilterOutputStream;
30import java.io.OutputStream;
31import java.io.InputStream;
32import java.io.IOException;
33
34/**
35 * This class implements an output stream filter for compressing data in
36 * the "deflate" compression format. It is also used as the basis for other
37 * types of compression filters, such as GZIPOutputStream.
38 *
39 * @see         Deflater
40 * @author      David Connelly
41 */
42public
43class DeflaterOutputStream extends FilterOutputStream {
44    /**
45     * Compressor for this stream.
46     */
47    protected Deflater def;
48
49    /**
50     * Output buffer for writing compressed data.
51     */
52    protected byte[] buf;
53
54    /**
55     * Indicates that the stream has been closed.
56     */
57
58    private boolean closed = false;
59
60    private final boolean syncFlush;
61
62    /**
63     * Creates a new output stream with the specified compressor,
64     * buffer size and flush mode.
65
66     * @param out the output stream
67     * @param def the compressor ("deflater")
68     * @param size the output buffer size
69     * @param syncFlush
70     *        if {@code true} the {@link #flush()} method of this
71     *        instance flushes the compressor with flush mode
72     *        {@link Deflater#SYNC_FLUSH} before flushing the output
73     *        stream, otherwise only flushes the output stream
74     *
75     * @throws IllegalArgumentException if size is <= 0
76     *
77     * @since 1.7
78     */
79    public DeflaterOutputStream(OutputStream out,
80                                Deflater def,
81                                int size,
82                                boolean syncFlush) {
83        super(out);
84        if (out == null || def == null) {
85            throw new NullPointerException();
86        } else if (size <= 0) {
87            throw new IllegalArgumentException("buffer size <= 0");
88        }
89        this.def = def;
90        this.buf = new byte[size];
91        this.syncFlush = syncFlush;
92    }
93
94
95    /**
96     * Creates a new output stream with the specified compressor and
97     * buffer size.
98     *
99     * <p>The new output stream instance is created as if by invoking
100     * the 4-argument constructor DeflaterOutputStream(out, def, size, false).
101     *
102     * @param out the output stream
103     * @param def the compressor ("deflater")
104     * @param size the output buffer size
105     * @exception IllegalArgumentException if size is <= 0
106     */
107    public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
108        this(out, def, size, false);
109    }
110
111    /**
112     * Creates a new output stream with the specified compressor, flush
113     * mode and a default buffer size.
114     *
115     * @param out the output stream
116     * @param def the compressor ("deflater")
117     * @param syncFlush
118     *        if {@code true} the {@link #flush()} method of this
119     *        instance flushes the compressor with flush mode
120     *        {@link Deflater#SYNC_FLUSH} before flushing the output
121     *        stream, otherwise only flushes the output stream
122     *
123     * @since 1.7
124     */
125    public DeflaterOutputStream(OutputStream out,
126                                Deflater def,
127                                boolean syncFlush) {
128        this(out, def, 512, syncFlush);
129    }
130
131
132    /**
133     * Creates a new output stream with the specified compressor and
134     * a default buffer size.
135     *
136     * <p>The new output stream instance is created as if by invoking
137     * the 3-argument constructor DeflaterOutputStream(out, def, false).
138     *
139     * @param out the output stream
140     * @param def the compressor ("deflater")
141     */
142    public DeflaterOutputStream(OutputStream out, Deflater def) {
143        this(out, def, 512, false);
144    }
145
146    boolean usesDefaultDeflater = false;
147
148
149    /**
150     * Creates a new output stream with a default compressor, a default
151     * buffer size and the specified flush mode.
152     *
153     * @param out the output stream
154     * @param syncFlush
155     *        if {@code true} the {@link #flush()} method of this
156     *        instance flushes the compressor with flush mode
157     *        {@link Deflater#SYNC_FLUSH} before flushing the output
158     *        stream, otherwise only flushes the output stream
159     *
160     * @since 1.7
161     */
162    public DeflaterOutputStream(OutputStream out, boolean syncFlush) {
163        this(out, new Deflater(), 512, syncFlush);
164        usesDefaultDeflater = true;
165    }
166
167    /**
168     * Creates a new output stream with a default compressor and buffer size.
169     *
170     * <p>The new output stream instance is created as if by invoking
171     * the 2-argument constructor DeflaterOutputStream(out, false).
172     *
173     * @param out the output stream
174     */
175    public DeflaterOutputStream(OutputStream out) {
176        this(out, false);
177        usesDefaultDeflater = true;
178    }
179
180    /**
181     * Writes a byte to the compressed output stream. This method will
182     * block until the byte can be written.
183     * @param b the byte to be written
184     * @exception IOException if an I/O error has occurred
185     */
186    public void write(int b) throws IOException {
187        byte[] buf = new byte[1];
188        buf[0] = (byte)(b & 0xff);
189        write(buf, 0, 1);
190    }
191
192    /**
193     * Writes an array of bytes to the compressed output stream. This
194     * method will block until all the bytes are written.
195     * @param b the data to be written
196     * @param off the start offset of the data
197     * @param len the length of the data
198     * @exception IOException if an I/O error has occurred
199     */
200    public void write(byte[] b, int off, int len) throws IOException {
201        if (def.finished()) {
202            throw new IOException("write beyond end of stream");
203        }
204        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
205            throw new IndexOutOfBoundsException();
206        } else if (len == 0) {
207            return;
208        }
209        if (!def.finished()) {
210            def.setInput(b, off, len);
211            while (!def.needsInput()) {
212                deflate();
213            }
214        }
215    }
216
217    /**
218     * Finishes writing compressed data to the output stream without closing
219     * the underlying stream. Use this method when applying multiple filters
220     * in succession to the same output stream.
221     * @exception IOException if an I/O error has occurred
222     */
223    public void finish() throws IOException {
224        if (!def.finished()) {
225            def.finish();
226            while (!def.finished()) {
227                deflate();
228            }
229        }
230    }
231
232    /**
233     * Writes remaining compressed data to the output stream and closes the
234     * underlying stream.
235     * @exception IOException if an I/O error has occurred
236     */
237    public void close() throws IOException {
238        if (!closed) {
239            finish();
240            if (usesDefaultDeflater)
241                def.end();
242            out.close();
243            closed = true;
244        }
245    }
246
247    /**
248     * Writes next block of compressed data to the output stream.
249     * @throws IOException if an I/O error has occurred
250     */
251    protected void deflate() throws IOException {
252        int len = 0;
253        while ((len = def.deflate(buf, 0, buf.length)) > 0) {
254          out.write(buf, 0, len);
255        }
256    }
257
258    /**
259     * Flushes the compressed output stream.
260     *
261     * If {@link #DeflaterOutputStream(OutputStream, Deflater, int, boolean)
262     * syncFlush} is {@code true} when this compressed output stream is
263     * constructed, this method first flushes the underlying {@code compressor}
264     * with the flush mode {@link Deflater#SYNC_FLUSH} to force
265     * all pending data to be flushed out to the output stream and then
266     * flushes the output stream. Otherwise this method only flushes the
267     * output stream without flushing the {@code compressor}.
268     *
269     * @throws IOException if an I/O error has occurred
270     *
271     * @since 1.7
272     */
273    public void flush() throws IOException {
274        if (syncFlush && !def.finished()) {
275            int len = 0;
276            while ((len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) > 0)
277            {
278                out.write(buf, 0, len);
279                if (len < buf.length)
280                    break;
281            }
282        }
283        out.flush();
284    }
285}
286