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