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