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