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 18package java.util.zip; 19 20import java.io.EOFException; 21import java.io.FilterInputStream; 22import java.io.IOException; 23import java.io.InputStream; 24import java.util.Arrays; 25import libcore.io.Streams; 26 27/** 28 * This class provides an implementation of {@code FilterInputStream} that 29 * decompresses data that was compressed using the <i>DEFLATE</i> algorithm 30 * (see <a href="http://www.gzip.org/algorithm.txt">specification</a>). 31 * Basically it wraps the {@code Inflater} class and takes care of the 32 * buffering. 33 * 34 * @see Inflater 35 * @see DeflaterOutputStream 36 */ 37public class InflaterInputStream extends FilterInputStream { 38 39 /** 40 * The inflater used for this stream. 41 */ 42 protected Inflater inf; 43 44 /** 45 * The input buffer used for decompression. 46 */ 47 protected byte[] buf; 48 49 /** 50 * The length of the buffer. 51 */ 52 protected int len; 53 54 boolean closed; 55 56 /** 57 * True if this stream's last byte has been returned to the user. This 58 * could be because the underlying stream has been exhausted, or if errors 59 * were encountered while inflating that stream. 60 */ 61 boolean eof; 62 63 static final int BUF_SIZE = 512; 64 65 int nativeEndBufSize = 0; 66 67 /** 68 * This is the most basic constructor. You only need to pass the {@code 69 * InputStream} from which the compressed data is to be read from. Default 70 * settings for the {@code Inflater} and internal buffer are be used. In 71 * particular the Inflater expects a ZLIB header from the input stream. 72 * 73 * @param is 74 * the {@code InputStream} to read data from. 75 */ 76 public InflaterInputStream(InputStream is) { 77 this(is, new Inflater(), BUF_SIZE); 78 } 79 80 /** 81 * This constructor lets you pass a specifically initialized Inflater, 82 * for example one that expects no ZLIB header. 83 * 84 * @param is 85 * the {@code InputStream} to read data from. 86 * @param inflater 87 * the specific {@code Inflater} for decompressing data. 88 */ 89 public InflaterInputStream(InputStream is, Inflater inflater) { 90 this(is, inflater, BUF_SIZE); 91 } 92 93 /** 94 * This constructor lets you specify both the {@code Inflater} as well as 95 * the internal buffer size to be used. 96 * 97 * @param is 98 * the {@code InputStream} to read data from. 99 * @param inflater 100 * the specific {@code Inflater} for decompressing data. 101 * @param bufferSize 102 * the size to be used for the internal buffer. 103 */ 104 public InflaterInputStream(InputStream is, Inflater inflater, int bufferSize) { 105 super(is); 106 if (is == null) { 107 throw new NullPointerException("is == null"); 108 } else if (inflater == null) { 109 throw new NullPointerException("inflater == null"); 110 } 111 if (bufferSize <= 0) { 112 throw new IllegalArgumentException("bufferSize <= 0: " + bufferSize); 113 } 114 this.inf = inflater; 115 if (is instanceof ZipFile.RAFStream) { 116 nativeEndBufSize = bufferSize; 117 } else { 118 buf = new byte[bufferSize]; 119 } 120 } 121 122 /** 123 * Reads a single byte of decompressed data. 124 * 125 * @return the byte read. 126 * @throws IOException 127 * if an error occurs reading the byte. 128 */ 129 @Override public int read() throws IOException { 130 return Streams.readSingleByte(this); 131 } 132 133 /** 134 * Reads up to {@code byteCount} bytes of decompressed data and stores it in 135 * {@code buffer} starting at {@code byteOffset}. Returns the number of uncompressed bytes read, 136 * or -1. 137 */ 138 @Override 139 public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 140 checkClosed(); 141 Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount); 142 143 if (byteCount == 0) { 144 return 0; 145 } 146 147 if (eof) { 148 return -1; 149 } 150 151 do { 152 if (inf.needsInput()) { 153 fill(); 154 } 155 // Invariant: if reading returns -1 or throws, eof must be true. 156 // It may also be true if the next read() should return -1. 157 try { 158 int result = inf.inflate(buffer, byteOffset, byteCount); 159 eof = inf.finished(); 160 if (result > 0) { 161 return result; 162 } else if (eof) { 163 return -1; 164 } else if (inf.needsDictionary()) { 165 eof = true; 166 return -1; 167 } else if (len == -1) { 168 eof = true; 169 throw new EOFException(); 170 // If result == 0, fill() and try again 171 } 172 } catch (DataFormatException e) { 173 eof = true; 174 if (len == -1) { 175 throw new EOFException(); 176 } 177 throw (IOException) (new IOException().initCause(e)); 178 } 179 } while (true); 180 } 181 182 /** 183 * Fills the input buffer with data to be decompressed. 184 * 185 * @throws IOException 186 * if an {@code IOException} occurs. 187 */ 188 protected void fill() throws IOException { 189 checkClosed(); 190 if (nativeEndBufSize > 0) { 191 ZipFile.RAFStream is = (ZipFile.RAFStream) in; 192 len = is.fill(inf, nativeEndBufSize); 193 } else { 194 if ((len = in.read(buf)) > 0) { 195 inf.setInput(buf, 0, len); 196 } 197 } 198 } 199 200 /** 201 * Skips up to {@code byteCount} bytes of uncompressed data. 202 * 203 * @param byteCount the number of bytes to skip. 204 * @return the number of uncompressed bytes skipped. 205 * @throws IllegalArgumentException if {@code byteCount < 0}. 206 * @throws IOException if an error occurs skipping. 207 */ 208 @Override 209 public long skip(long byteCount) throws IOException { 210 if (byteCount < 0) { 211 throw new IllegalArgumentException("byteCount < 0"); 212 } 213 return Streams.skipByReading(this, byteCount); 214 } 215 216 /** 217 * Returns 0 when when this stream has exhausted its input; and 1 otherwise. 218 * A result of 1 does not guarantee that further bytes can be returned, 219 * with or without blocking. 220 * 221 * <p>Although consistent with the RI, this behavior is inconsistent with 222 * {@link InputStream#available()}, and violates the <a 223 * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov 224 * Substitution Principle</a>. This method should not be used. 225 * 226 * @return 0 if no further bytes are available. Otherwise returns 1, 227 * which suggests (but does not guarantee) that additional bytes are 228 * available. 229 * @throws IOException if this stream is closed or an error occurs 230 */ 231 @Override 232 public int available() throws IOException { 233 checkClosed(); 234 if (eof) { 235 return 0; 236 } 237 return 1; 238 } 239 240 /** 241 * Closes the input stream. 242 * 243 * @throws IOException 244 * If an error occurs closing the input stream. 245 */ 246 @Override 247 public void close() throws IOException { 248 if (!closed) { 249 inf.end(); 250 closed = true; 251 eof = true; 252 super.close(); 253 } 254 } 255 256 /** 257 * Marks the current position in the stream. This implementation overrides 258 * the super type implementation to do nothing at all. 259 * 260 * @param readlimit 261 * of no use. 262 */ 263 @Override 264 public void mark(int readlimit) { 265 // do nothing 266 } 267 268 /** 269 * This operation is not supported and throws {@code IOException}. 270 */ 271 @Override 272 public void reset() throws IOException { 273 throw new IOException(); 274 } 275 276 /** 277 * Returns whether the receiver implements {@code mark} semantics. This type 278 * does not support {@code mark()}, so always responds {@code false}. 279 * 280 * @return false, always 281 */ 282 @Override 283 public boolean markSupported() { 284 return false; 285 } 286 287 private void checkClosed() throws IOException { 288 if (closed) { 289 throw new IOException("Stream is closed"); 290 } 291 } 292} 293