1 /*
2  * Copyright (c) 1996, 2021, 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 class 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 static final int GZIP_MAGIC = 0x8b1f;
48 
49     /*
50      * Trailer size in bytes.
51      *
52      */
53     private static final int TRAILER_SIZE = 8;
54 
55     // Represents the default "unknown" value for OS header, per RFC-1952
56     private static final byte OS_UNKNOWN = (byte) 255;
57 
58     /**
59      * Creates a new output stream with the specified buffer size.
60      *
61      * <p>The new output stream instance is created as if by invoking
62      * the 3-argument constructor GZIPOutputStream(out, size, false).
63      *
64      * Android-note: Android limits the number of UnbufferedIO operations that can be performed, so
65      * consider using buffered inputs with this class. More information can be found in the
66      * <a href="https://developer.android.com/reference/android/os/StrictMode.ThreadPolicy.Builder#detectUnbufferedIo()">
67      * UnbufferedIO</a> and
68      * <a href="https://developer.android.com/reference/android/os/StrictMode"> StrictMode</a>
69      * documentation.
70      *
71      * @param out the output stream
72      * @param size the output buffer size
73      * @throws    IOException If an I/O error has occurred.
74      * @throws    IllegalArgumentException if {@code size <= 0}
75      */
GZIPOutputStream(OutputStream out, int size)76     public GZIPOutputStream(OutputStream out, int size) throws IOException {
77         this(out, size, false);
78     }
79 
80     /**
81      * Creates a new output stream with the specified buffer size and
82      * flush mode.
83      *
84      * @param out the output stream
85      * @param size the output buffer size
86      * @param syncFlush
87      *        if {@code true} invocation of the inherited
88      *        {@link DeflaterOutputStream#flush() flush()} method of
89      *        this instance flushes the compressor with flush mode
90      *        {@link Deflater#SYNC_FLUSH} before flushing the output
91      *        stream, otherwise only flushes the output stream
92      * @throws    IOException If an I/O error has occurred.
93      * @throws    IllegalArgumentException if {@code size <= 0}
94      *
95      * @since 1.7
96      */
GZIPOutputStream(OutputStream out, int size, boolean syncFlush)97     public GZIPOutputStream(OutputStream out, int size, boolean syncFlush)
98         throws IOException
99     {
100         super(out, out != null ? new Deflater(Deflater.DEFAULT_COMPRESSION, true) : null,
101               size,
102               syncFlush);
103         usesDefaultDeflater = true;
104         writeHeader();
105         crc.reset();
106     }
107 
108 
109     /**
110      * Creates a new output stream with a default buffer size.
111      *
112      * <p>The new output stream instance is created as if by invoking
113      * the 2-argument constructor GZIPOutputStream(out, false).
114      *
115      * @param out the output stream
116      * @throws    IOException If an I/O error has occurred.
117      */
GZIPOutputStream(OutputStream out)118     public GZIPOutputStream(OutputStream out) throws IOException {
119         this(out, 512, false);
120     }
121 
122     /**
123      * Creates a new output stream with a default buffer size and
124      * the specified flush mode.
125      *
126      * @param out the output stream
127      * @param syncFlush
128      *        if {@code true} invocation of the inherited
129      *        {@link DeflaterOutputStream#flush() flush()} method of
130      *        this instance flushes the compressor with flush mode
131      *        {@link Deflater#SYNC_FLUSH} before flushing the output
132      *        stream, otherwise only flushes the output stream
133      *
134      * @throws    IOException If an I/O error has occurred.
135      *
136      * @since 1.7
137      */
GZIPOutputStream(OutputStream out, boolean syncFlush)138     public GZIPOutputStream(OutputStream out, boolean syncFlush)
139         throws IOException
140     {
141         this(out, 512, syncFlush);
142     }
143 
144     /**
145      * Writes array of bytes to the compressed output stream. This method
146      * will block until all the bytes are written.
147      * @param buf the data to be written
148      * @param off the start offset of the data
149      * @param len the length of the data
150      * @throws    IOException If an I/O error has occurred.
151      */
write(byte[] buf, int off, int len)152     public synchronized void write(byte[] buf, int off, int len)
153         throws IOException
154     {
155         super.write(buf, off, len);
156         crc.update(buf, off, len);
157     }
158 
159     /**
160      * Finishes writing compressed data to the output stream without closing
161      * the underlying stream. Use this method when applying multiple filters
162      * in succession to the same output stream.
163      * @throws    IOException if an I/O error has occurred
164      */
finish()165     public void finish() throws IOException {
166         if (!def.finished()) {
167             try {
168                 def.finish();
169                 while (!def.finished()) {
170                     int len = def.deflate(buf, 0, buf.length);
171                     if (def.finished() && len <= buf.length - TRAILER_SIZE) {
172                         // last deflater buffer. Fit trailer at the end
173                         writeTrailer(buf, len);
174                         len = len + TRAILER_SIZE;
175                         out.write(buf, 0, len);
176                         return;
177                     }
178                     if (len > 0)
179                         out.write(buf, 0, len);
180                 }
181                 // if we can't fit the trailer at the end of the last
182                 // deflater buffer, we write it separately
183                 byte[] trailer = new byte[TRAILER_SIZE];
184                 writeTrailer(trailer, 0);
185                 out.write(trailer);
186             } catch (IOException e) {
187                 if (usesDefaultDeflater)
188                     def.end();
189                 throw e;
190             }
191         }
192     }
193 
194     /*
195      * Writes GZIP member header.
196      */
writeHeader()197     private void writeHeader() throws IOException {
198         out.write(new byte[] {
199                       (byte) GZIP_MAGIC,        // Magic number (short)
200                       (byte)(GZIP_MAGIC >> 8),  // Magic number (short)
201                       Deflater.DEFLATED,        // Compression method (CM)
202                       0,                        // Flags (FLG)
203                       0,                        // Modification time MTIME (int)
204                       0,                        // Modification time MTIME (int)
205                       0,                        // Modification time MTIME (int)
206                       0,                        // Modification time MTIME (int)
207                       0,                        // Extra flags (XFLG)
208                       OS_UNKNOWN                // Operating system (OS)
209                   });
210     }
211 
212     /*
213      * Writes GZIP member trailer to a byte array, starting at a given
214      * offset.
215      */
writeTrailer(byte[] buf, int offset)216     private void writeTrailer(byte[] buf, int offset) throws IOException {
217         writeInt((int)crc.getValue(), buf, offset); // CRC-32 of uncompr. data
218         writeInt(def.getTotalIn(), buf, offset + 4); // Number of uncompr. bytes
219     }
220 
221     /*
222      * Writes integer in Intel byte order to a byte array, starting at a
223      * given offset.
224      */
writeInt(int i, byte[] buf, int offset)225     private void writeInt(int i, byte[] buf, int offset) throws IOException {
226         writeShort(i & 0xffff, buf, offset);
227         writeShort((i >> 16) & 0xffff, buf, offset + 2);
228     }
229 
230     /*
231      * Writes short integer in Intel byte order to a byte array, starting
232      * at a given offset
233      */
writeShort(int s, byte[] buf, int offset)234     private void writeShort(int s, byte[] buf, int offset) throws IOException {
235         buf[offset] = (byte)(s & 0xff);
236         buf[offset + 1] = (byte)((s >> 8) & 0xff);
237     }
238 }
239