1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.util.zip; 19 20 import java.io.FilterOutputStream; 21 import java.io.IOException; 22 import java.io.OutputStream; 23 import java.util.Arrays; 24 import libcore.io.Streams; 25 26 /** 27 * This class provides an implementation of {@code FilterOutputStream} that 28 * compresses data using the <i>DEFLATE</i> algorithm. Basically it wraps the 29 * {@code Deflater} class and takes care of the buffering. 30 * 31 * @see Deflater 32 */ 33 public class DeflaterOutputStream extends FilterOutputStream { 34 static final int BUF_SIZE = 512; 35 36 /** 37 * The buffer for the data to be written to. 38 */ 39 protected byte[] buf; 40 41 /** 42 * The deflater used. 43 */ 44 protected Deflater def; 45 46 boolean done = false; 47 48 private final boolean syncFlush; 49 50 /** 51 * This constructor lets you pass the {@code Deflater} specifying the 52 * compression algorithm. 53 * 54 * @param os 55 * is the {@code OutputStream} where to write the compressed data 56 * to. 57 * @param def 58 * is the specific {@code Deflater} that is used to compress 59 * data. 60 */ 61 public DeflaterOutputStream(OutputStream os, Deflater def) { 62 this(os, def, BUF_SIZE, false); 63 } 64 65 /** 66 * This is the most basic constructor. You only need to pass the {@code 67 * OutputStream} to which the compressed data shall be written to. The 68 * default settings for the {@code Deflater} and internal buffer are used. 69 * In particular the {@code Deflater} produces a ZLIB header in the output 70 * stream. 71 * 72 * @param os 73 * is the OutputStream where to write the compressed data to. 74 */ 75 public DeflaterOutputStream(OutputStream os) { 76 this(os, new Deflater(), BUF_SIZE, false); 77 } 78 79 /** 80 * This constructor lets you specify both the compression algorithm as well 81 * as the internal buffer size to be used. 82 * 83 * @param os 84 * is the {@code OutputStream} where to write the compressed data 85 * to. 86 * @param def 87 * is the specific {@code Deflater} that will be used to compress 88 * data. 89 * @param bufferSize 90 * is the size to be used for the internal buffer. 91 */ 92 public DeflaterOutputStream(OutputStream os, Deflater def, int bufferSize) { 93 this(os, def, bufferSize, false); 94 } 95 96 /** 97 * @hide 98 * @since 1.7 99 */ 100 public DeflaterOutputStream(OutputStream os, boolean syncFlush) { 101 this(os, new Deflater(), BUF_SIZE, syncFlush); 102 } 103 104 /** 105 * @hide 106 * @since 1.7 107 */ 108 public DeflaterOutputStream(OutputStream os, Deflater def, boolean syncFlush) { 109 this(os, def, BUF_SIZE, syncFlush); 110 } 111 112 /** 113 * @hide 114 * @since 1.7 115 */ 116 public DeflaterOutputStream(OutputStream os, Deflater def, int bufferSize, boolean syncFlush) { 117 super(os); 118 if (os == null) { 119 throw new NullPointerException("os == null"); 120 } else if (def == null) { 121 throw new NullPointerException("def == null"); 122 } 123 if (bufferSize <= 0) { 124 throw new IllegalArgumentException("bufferSize <= 0: " + bufferSize); 125 } 126 this.def = def; 127 this.syncFlush = syncFlush; 128 buf = new byte[bufferSize]; 129 } 130 131 /** 132 * Compress the data in the input buffer and write it to the underlying 133 * stream. 134 * 135 * @throws IOException 136 * If an error occurs during deflation. 137 */ 138 protected void deflate() throws IOException { 139 int byteCount; 140 while ((byteCount = def.deflate(buf)) != 0) { 141 out.write(buf, 0, byteCount); 142 } 143 } 144 145 /** 146 * Writes any unwritten compressed data to the underlying stream, the closes 147 * all underlying streams. This stream can no longer be used after close() 148 * has been called. 149 * 150 * @throws IOException 151 * If an error occurs while closing the data compression 152 * process. 153 */ 154 @Override 155 public void close() throws IOException { 156 // everything closed here should also be closed in ZipOutputStream.close() 157 if (!def.finished()) { 158 finish(); 159 } 160 def.end(); 161 out.close(); 162 } 163 164 /** 165 * Writes any unwritten data to the underlying stream. Does not close the 166 * stream. 167 * 168 * @throws IOException 169 * If an error occurs. 170 */ 171 public void finish() throws IOException { 172 if (done) { 173 return; 174 } 175 def.finish(); 176 while (!def.finished()) { 177 int byteCount = def.deflate(buf); 178 out.write(buf, 0, byteCount); 179 } 180 done = true; 181 } 182 183 @Override public void write(int i) throws IOException { 184 Streams.writeSingleByte(this, i); 185 } 186 187 /** 188 * Compresses {@code byteCount} bytes of data from {@code buf} starting at 189 * {@code offset} and writes it to the underlying stream. 190 * @throws IOException 191 * If an error occurs during writing. 192 */ 193 @Override public void write(byte[] buffer, int offset, int byteCount) throws IOException { 194 if (done) { 195 throw new IOException("attempt to write after finish"); 196 } 197 Arrays.checkOffsetAndCount(buffer.length, offset, byteCount); 198 if (!def.needsInput()) { 199 throw new IOException(); 200 } 201 def.setInput(buffer, offset, byteCount); 202 deflate(); 203 } 204 205 /** 206 * Flushes the underlying stream. This flushes only the bytes that can be 207 * compressed at the highest level. 208 * 209 * <p>For deflater output streams constructed with Java 7's 210 * {@code syncFlush} parameter set to true (not yet available on Android), 211 * this first flushes all outstanding data so that it may be immediately 212 * read by its recipient. Doing so may degrade compression. 213 */ 214 @Override public void flush() throws IOException { 215 if (syncFlush) { 216 int byteCount; 217 while ((byteCount = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) != 0) { 218 out.write(buf, 0, byteCount); 219 } 220 } 221 out.flush(); 222 } 223 } 224