1 /* 2 * Copyright (c) 1996, 2019, 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.FilterOutputStream; 29 import java.io.OutputStream; 30 import java.io.InputStream; 31 import java.io.IOException; 32 33 /** 34 * This class implements an output stream filter for compressing data in 35 * the "deflate" compression format. It is also used as the basis for other 36 * types of compression filters, such as GZIPOutputStream. 37 * 38 * @see Deflater 39 * @author David Connelly 40 * @since 1.1 41 */ 42 public class DeflaterOutputStream extends FilterOutputStream { 43 /** 44 * Compressor for this stream. 45 */ 46 protected Deflater def; 47 48 /** 49 * Output buffer for writing compressed data. 50 */ 51 protected byte[] buf; 52 53 /** 54 * Indicates that the stream has been closed. 55 */ 56 private boolean closed = false; 57 58 private final boolean syncFlush; 59 60 /** 61 * Creates a new output stream with the specified compressor, 62 * buffer size and flush mode. 63 * 64 * @param out the output stream 65 * @param def the compressor ("deflater") 66 * @param size the output buffer size 67 * @param syncFlush 68 * if {@code true} the {@link #flush()} method of this 69 * instance flushes the compressor with flush mode 70 * {@link Deflater#SYNC_FLUSH} before flushing the output 71 * stream, otherwise only flushes the output stream 72 * 73 * @throws IllegalArgumentException if {@code size <= 0} 74 * 75 * @since 1.7 76 */ DeflaterOutputStream(OutputStream out, Deflater def, int size, boolean syncFlush)77 public DeflaterOutputStream(OutputStream out, 78 Deflater def, 79 int size, 80 boolean syncFlush) { 81 super(out); 82 if (out == null || def == null) { 83 throw new NullPointerException(); 84 } else if (size <= 0) { 85 throw new IllegalArgumentException("buffer size <= 0"); 86 } 87 this.def = def; 88 this.buf = new byte[size]; 89 this.syncFlush = syncFlush; 90 } 91 92 93 /** 94 * Creates a new output stream with the specified compressor and 95 * buffer size. 96 * 97 * <p>The new output stream instance is created as if by invoking 98 * the 4-argument constructor DeflaterOutputStream(out, def, size, false). 99 * 100 * @param out the output stream 101 * @param def the compressor ("deflater") 102 * @param size the output buffer size 103 * @throws IllegalArgumentException if {@code size <= 0} 104 */ DeflaterOutputStream(OutputStream out, Deflater def, int size)105 public DeflaterOutputStream(OutputStream out, Deflater def, int size) { 106 this(out, def, size, false); 107 } 108 109 /** 110 * Creates a new output stream with the specified compressor, flush 111 * mode and a default buffer size. 112 * 113 * @param out the output stream 114 * @param def the compressor ("deflater") 115 * @param syncFlush 116 * if {@code true} the {@link #flush()} method of this 117 * instance flushes the compressor with flush mode 118 * {@link Deflater#SYNC_FLUSH} before flushing the output 119 * stream, otherwise only flushes the output stream 120 * 121 * @since 1.7 122 */ DeflaterOutputStream(OutputStream out, Deflater def, boolean syncFlush)123 public DeflaterOutputStream(OutputStream out, 124 Deflater def, 125 boolean syncFlush) { 126 this(out, def, 512, syncFlush); 127 } 128 129 130 /** 131 * Creates a new output stream with the specified compressor and 132 * a default buffer size. 133 * 134 * <p>The new output stream instance is created as if by invoking 135 * the 3-argument constructor DeflaterOutputStream(out, def, false). 136 * 137 * @param out the output stream 138 * @param def the compressor ("deflater") 139 */ DeflaterOutputStream(OutputStream out, Deflater def)140 public DeflaterOutputStream(OutputStream out, Deflater def) { 141 this(out, def, 512, false); 142 } 143 144 boolean usesDefaultDeflater = false; 145 146 147 /** 148 * Creates a new output stream with a default compressor, a default 149 * buffer size and the specified flush mode. 150 * 151 * @param out the output stream 152 * @param syncFlush 153 * if {@code true} the {@link #flush()} method of this 154 * instance flushes the compressor with flush mode 155 * {@link Deflater#SYNC_FLUSH} before flushing the output 156 * stream, otherwise only flushes the output stream 157 * 158 * @since 1.7 159 */ DeflaterOutputStream(OutputStream out, boolean syncFlush)160 public DeflaterOutputStream(OutputStream out, boolean syncFlush) { 161 this(out, new Deflater(), 512, syncFlush); 162 usesDefaultDeflater = true; 163 } 164 165 /** 166 * Creates a new output stream with a default compressor and buffer size. 167 * 168 * <p>The new output stream instance is created as if by invoking 169 * the 2-argument constructor DeflaterOutputStream(out, false). 170 * 171 * @param out the output stream 172 */ DeflaterOutputStream(OutputStream out)173 public DeflaterOutputStream(OutputStream out) { 174 this(out, false); 175 usesDefaultDeflater = true; 176 } 177 178 /** 179 * Writes a byte to the compressed output stream. This method will 180 * block until the byte can be written. 181 * @param b the byte to be written 182 * @throws IOException if an I/O error has occurred 183 */ write(int b)184 public void write(int b) throws IOException { 185 byte[] buf = new byte[1]; 186 buf[0] = (byte)(b & 0xff); 187 write(buf, 0, 1); 188 } 189 190 /** 191 * Writes an array of bytes to the compressed output stream. This 192 * method will block until all the bytes are written. 193 * @param b the data to be written 194 * @param off the start offset of the data 195 * @param len the length of the data 196 * @throws IOException if an I/O error has occurred 197 */ write(byte[] b, int off, int len)198 public void write(byte[] b, int off, int len) throws IOException { 199 if (def.finished()) { 200 throw new IOException("write beyond end of stream"); 201 } 202 if ((off | len | (off + len) | (b.length - (off + len))) < 0) { 203 throw new IndexOutOfBoundsException(); 204 } else if (len == 0) { 205 return; 206 } 207 if (!def.finished()) { 208 def.setInput(b, off, len); 209 while (!def.needsInput()) { 210 deflate(); 211 } 212 } 213 } 214 215 /** 216 * Finishes writing compressed data to the output stream without closing 217 * the underlying stream. Use this method when applying multiple filters 218 * in succession to the same output stream. 219 * @throws IOException if an I/O error has occurred 220 */ finish()221 public void finish() throws IOException { 222 if (!def.finished()) { 223 def.finish(); 224 while (!def.finished()) { 225 deflate(); 226 } 227 } 228 } 229 230 /** 231 * Writes remaining compressed data to the output stream and closes the 232 * underlying stream. 233 * @throws IOException if an I/O error has occurred 234 */ close()235 public void close() throws IOException { 236 if (!closed) { 237 finish(); 238 if (usesDefaultDeflater) 239 def.end(); 240 out.close(); 241 closed = true; 242 } 243 } 244 245 /** 246 * Writes next block of compressed data to the output stream. 247 * @throws IOException if an I/O error has occurred 248 */ deflate()249 protected void deflate() throws IOException { 250 int len = def.deflate(buf, 0, buf.length); 251 if (len > 0) { 252 out.write(buf, 0, len); 253 } 254 } 255 256 /** 257 * Flushes the compressed output stream. 258 * 259 * If {@link #DeflaterOutputStream(OutputStream, Deflater, int, boolean) 260 * syncFlush} is {@code true} when this compressed output stream is 261 * constructed, this method first flushes the underlying {@code compressor} 262 * with the flush mode {@link Deflater#SYNC_FLUSH} to force 263 * all pending data to be flushed out to the output stream and then 264 * flushes the output stream. Otherwise this method only flushes the 265 * output stream without flushing the {@code compressor}. 266 * 267 * @throws IOException if an I/O error has occurred 268 * 269 * @since 1.7 270 */ flush()271 public void flush() throws IOException { 272 if (syncFlush && !def.finished()) { 273 int len = 0; 274 while ((len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) > 0) 275 { 276 out.write(buf, 0, len); 277 if (len < buf.length) 278 break; 279 } 280 } 281 out.flush(); 282 } 283 } 284