InflaterInputStream.java revision cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd
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; 24 25import org.apache.harmony.archive.internal.nls.Messages; 26 27/** 28 * This class provides an implementation of {@code FilterInputStream} that 29 * uncompresses 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; // android-only 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 inf 87 * the specific {@code Inflater} for uncompressing data. 88 */ 89 public InflaterInputStream(InputStream is, Inflater inf) { 90 this(is, inf, 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 inf 100 * the specific {@code Inflater} for uncompressing data. 101 * @param bsize 102 * the size to be used for the internal buffer. 103 */ 104 public InflaterInputStream(InputStream is, Inflater inf, int bsize) { 105 super(is); 106 if (is == null || inf == null) { 107 throw new NullPointerException(); 108 } 109 if (bsize <= 0) { 110 throw new IllegalArgumentException(); 111 } 112 this.inf = inf; 113 // BEGIN android-only 114 if (is instanceof ZipFile.RAFStream) { 115 nativeEndBufSize = bsize; 116 } else { 117 buf = new byte[bsize]; 118 } 119 // END android-only 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 130 public int read() throws IOException { 131 byte[] b = new byte[1]; 132 if (read(b, 0, 1) == -1) { 133 return -1; 134 } 135 return b[0] & 0xff; 136 } 137 138 /** 139 * Reads up to {@code nbytes} of decompressed data and stores it in 140 * {@code buffer} starting at {@code off}. 141 * 142 * @param buffer 143 * the buffer to write data to. 144 * @param off 145 * offset in buffer to start writing. 146 * @param nbytes 147 * number of bytes to read. 148 * @return Number of uncompressed bytes read 149 * @throws IOException 150 * if an IOException occurs. 151 */ 152 @Override 153 public int read(byte[] buffer, int off, int nbytes) throws IOException { 154 checkClosed(); 155 if (buffer == null) { 156 throw new NullPointerException(); 157 } 158 159 if (off < 0 || nbytes < 0 || off + nbytes > buffer.length) { 160 throw new IndexOutOfBoundsException(); 161 } 162 163 if (nbytes == 0) { 164 return 0; 165 } 166 167 if (eof) { 168 return -1; 169 } 170 171 // avoid int overflow, check null buffer 172 if (off > buffer.length || nbytes < 0 || off < 0 173 || buffer.length - off < nbytes) { 174 throw new ArrayIndexOutOfBoundsException(); 175 } 176 177 do { 178 if (inf.needsInput()) { 179 fill(); 180 } 181 // Invariant: if reading returns -1 or throws, eof must be true. 182 // It may also be true if the next read() should return -1. 183 try { 184 int result = inf.inflate(buffer, off, nbytes); 185 eof = inf.finished(); 186 if (result > 0) { 187 return result; 188 } else if (eof) { 189 return -1; 190 } else if (inf.needsDictionary()) { 191 eof = true; 192 return -1; 193 } else if (len == -1) { 194 eof = true; 195 throw new EOFException(); 196 // If result == 0, fill() and try again 197 } 198 } catch (DataFormatException e) { 199 eof = true; 200 if (len == -1) { 201 throw new EOFException(); 202 } 203 throw (IOException) (new IOException().initCause(e)); 204 } 205 } while (true); 206 } 207 208 /** 209 * Fills the input buffer with data to be decompressed. 210 * 211 * @throws IOException 212 * if an {@code IOException} occurs. 213 */ 214 protected void fill() throws IOException { 215 checkClosed(); 216 // BEGIN android-only 217 if (nativeEndBufSize > 0) { 218 ZipFile.RAFStream is = (ZipFile.RAFStream)in; 219 synchronized (is.mSharedRaf) { 220 long len = is.mLength - is.mOffset; 221 if (len > nativeEndBufSize) len = nativeEndBufSize; 222 int cnt = inf.setFileInput(is.mSharedRaf.getFD(), is.mOffset, (int)nativeEndBufSize); 223 is.skip(cnt); 224 } 225 } else { 226 if ((len = in.read(buf)) > 0) { 227 inf.setInput(buf, 0, len); 228 } 229 } 230 // END android-only 231 } 232 233 /** 234 * Skips up to n bytes of uncompressed data. 235 * 236 * @param nbytes 237 * the number of bytes to skip. 238 * @return the number of uncompressed bytes skipped. 239 * @throws IOException 240 * if an error occurs skipping. 241 */ 242 @Override 243 public long skip(long nbytes) throws IOException { 244 if (nbytes >= 0) { 245 if (buf == null) { 246 buf = new byte[(int)Math.min(nbytes, BUF_SIZE)]; 247 } 248 long count = 0, rem = 0; 249 while (count < nbytes) { 250 int x = read(buf, 0, 251 (rem = nbytes - count) > buf.length ? buf.length 252 : (int) rem); 253 if (x == -1) { 254 return count; 255 } 256 count += x; 257 } 258 return count; 259 } 260 throw new IllegalArgumentException(); 261 } 262 263 /** 264 * Returns 0 when when this stream has exhausted its input; and 1 otherwise. 265 * A result of 1 does not guarantee that further bytes can be returned, 266 * with or without blocking. 267 * 268 * <p>Although consistent with the RI, this behavior is inconsistent with 269 * {@link InputStream#available()}, and violates the <a 270 * href="http://en.wikipedia.org/wiki/Liskov_substitution_principle">Liskov 271 * Substitution Principle</a>. This method should not be used. 272 * 273 * @return 0 if no further bytes are available. Otherwise returns 1, 274 * which suggests (but does not guarantee) that additional bytes are 275 * available. 276 * @throws IOException if this stream is closed or an error occurs 277 */ 278 @Override 279 public int available() throws IOException { 280 checkClosed(); 281 if (eof) { 282 return 0; 283 } 284 return 1; 285 } 286 287 /** 288 * Closes the input stream. 289 * 290 * @throws IOException 291 * If an error occurs closing the input stream. 292 */ 293 @Override 294 public void close() throws IOException { 295 if (!closed) { 296 inf.end(); 297 closed = true; 298 eof = true; 299 super.close(); 300 } 301 } 302 303 /** 304 * Marks the current position in the stream. This implementation overrides 305 * the super type implementation to do nothing at all. 306 * 307 * @param readlimit 308 * of no use. 309 */ 310 @Override 311 public void mark(int readlimit) { 312 // do nothing 313 } 314 315 /** 316 * Reset the position of the stream to the last marked position. This 317 * implementation overrides the supertype implementation and always throws 318 * an {@link IOException IOException} when called. 319 * 320 * @throws IOException 321 * if the method is called 322 */ 323 @Override 324 public void reset() throws IOException { 325 throw new IOException(); 326 } 327 328 /** 329 * Returns whether the receiver implements {@code mark} semantics. This type 330 * does not support {@code mark()}, so always responds {@code false}. 331 * 332 * @return false, always 333 */ 334 @Override 335 public boolean markSupported() { 336 return false; 337 } 338 339 private void checkClosed() throws IOException { 340 if (closed) { 341 throw new IOException("Stream is closed"); 342 } 343 } 344} 345