1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.util.zip; 28 29 import java.io.FilterInputStream; 30 import java.io.InputStream; 31 import java.io.IOException; 32 import java.io.EOFException; 33 34 /** 35 * This class implements a stream filter for uncompressing data in the 36 * "deflate" compression format. It is also used as the basis for other 37 * decompression filters, such as GZIPInputStream. 38 * 39 * @see Inflater 40 * @author David Connelly 41 */ 42 public 43 class InflaterInputStream extends FilterInputStream { 44 /** 45 * Decompressor for this stream. 46 */ 47 protected Inflater inf; 48 49 /** 50 * Input buffer for decompression. 51 */ 52 protected byte[] buf; 53 54 /** 55 * Length of input buffer. 56 */ 57 protected int len; 58 59 // Android-changed: Make closed accessible to subclasses. 60 // This was made protected because it needed to be accessed by 61 // StrictJarFile.ZipInflaterInputStream. Unfortunately, it was not marked as @hide and so it 62 // inadvertently became part of the public API. It will be marked as @removed to remove it from 63 // the public API in a future release of Android. See http://b/111592689 for more information. 64 // private boolean closed = false; 65 /** 66 * Indicates whether the {@link #close()} method has been called, internal use only. 67 * 68 * @deprecated This field will be removed from a future version of Android and should not be 69 * used. Subclasses that access this field need to be modified to keep track of their own 70 * closed state by overriding close(). 71 */ 72 @Deprecated 73 protected boolean closed = false; 74 75 // this flag is set to true after EOF has reached 76 private boolean reachEOF = false; 77 78 /** 79 * Check to make sure that this stream has not been closed 80 */ ensureOpen()81 private void ensureOpen() throws IOException { 82 if (closed) { 83 throw new IOException("Stream closed"); 84 } 85 } 86 87 88 /** 89 * Creates a new input stream with the specified decompressor and 90 * buffer size. 91 * @param in the input stream 92 * @param inf the decompressor ("inflater") 93 * @param size the input buffer size 94 * @exception IllegalArgumentException if {@code size <= 0} 95 */ InflaterInputStream(InputStream in, Inflater inf, int size)96 public InflaterInputStream(InputStream in, Inflater inf, int size) { 97 super(in); 98 if (in == null || inf == null) { 99 throw new NullPointerException(); 100 } else if (size <= 0) { 101 throw new IllegalArgumentException("buffer size <= 0"); 102 } 103 this.inf = inf; 104 buf = new byte[size]; 105 } 106 107 /** 108 * Creates a new input stream with the specified decompressor and a 109 * default buffer size. 110 * @param in the input stream 111 * @param inf the decompressor ("inflater") 112 */ InflaterInputStream(InputStream in, Inflater inf)113 public InflaterInputStream(InputStream in, Inflater inf) { 114 this(in, inf, 512); 115 } 116 117 // Android-changed: Unconditionally close external inflaters (b/26462400) 118 // See http://b/111630946 for more details. 119 // boolean usesDefaultInflater = false; 120 121 /** 122 * Creates a new input stream with a default decompressor and buffer size. 123 * @param in the input stream 124 */ InflaterInputStream(InputStream in)125 public InflaterInputStream(InputStream in) { 126 this(in, new Inflater()); 127 // Android-changed: Unconditionally close external inflaters (b/26462400) 128 // usesDefaultInflater = true; 129 } 130 131 private byte[] singleByteBuf = new byte[1]; 132 133 /** 134 * Reads a byte of uncompressed data. This method will block until 135 * enough input is available for decompression. 136 * @return the byte read, or -1 if end of compressed input is reached 137 * @exception IOException if an I/O error has occurred 138 */ read()139 public int read() throws IOException { 140 ensureOpen(); 141 return read(singleByteBuf, 0, 1) == -1 ? -1 : Byte.toUnsignedInt(singleByteBuf[0]); 142 } 143 144 /** 145 * Reads uncompressed data into an array of bytes. If <code>len</code> is not 146 * zero, the method will block until some input can be decompressed; otherwise, 147 * no bytes are read and <code>0</code> is returned. 148 * @param b the buffer into which the data is read 149 * @param off the start offset in the destination array <code>b</code> 150 * @param len the maximum number of bytes read 151 * @return the actual number of bytes read, or -1 if the end of the 152 * compressed input is reached or a preset dictionary is needed 153 * @exception NullPointerException If <code>b</code> is <code>null</code>. 154 * @exception IndexOutOfBoundsException If <code>off</code> is negative, 155 * <code>len</code> is negative, or <code>len</code> is greater than 156 * <code>b.length - off</code> 157 * @exception ZipException if a ZIP format error has occurred 158 * @exception IOException if an I/O error has occurred 159 */ read(byte[] b, int off, int len)160 public int read(byte[] b, int off, int len) throws IOException { 161 ensureOpen(); 162 if (b == null) { 163 throw new NullPointerException(); 164 } else if (off < 0 || len < 0 || len > b.length - off) { 165 throw new IndexOutOfBoundsException(); 166 } else if (len == 0) { 167 return 0; 168 } 169 try { 170 int n; 171 while ((n = inf.inflate(b, off, len)) == 0) { 172 if (inf.finished() || inf.needsDictionary()) { 173 reachEOF = true; 174 return -1; 175 } 176 if (inf.needsInput()) { 177 fill(); 178 } 179 } 180 return n; 181 } catch (DataFormatException e) { 182 String s = e.getMessage(); 183 throw new ZipException(s != null ? s : "Invalid ZLIB data format"); 184 } 185 } 186 187 /** 188 * Returns 0 after EOF has been reached, otherwise always return 1. 189 * <p> 190 * Programs should not count on this method to return the actual number 191 * of bytes that could be read without blocking. 192 * 193 * @return 1 before EOF and 0 after EOF. 194 * @exception IOException if an I/O error occurs. 195 * 196 */ available()197 public int available() throws IOException { 198 ensureOpen(); 199 if (reachEOF) { 200 return 0; 201 // BEGIN Android-added: Return more accurate value from available(). 202 // Integrates change http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/dbcf47bfb044 made as part 203 // of https://bugs.openjdk.java.net/browse/JDK-7031075. 204 } else if (inf.finished()) { 205 // the end of the compressed data stream has been reached 206 reachEOF = true; 207 return 0; 208 // END Android-added: Return more accurate value from available(). 209 } else { 210 return 1; 211 } 212 } 213 214 private byte[] b = new byte[512]; 215 216 /** 217 * Skips specified number of bytes of uncompressed data. 218 * @param n the number of bytes to skip 219 * @return the actual number of bytes skipped. 220 * @exception IOException if an I/O error has occurred 221 * @exception IllegalArgumentException if {@code n < 0} 222 */ skip(long n)223 public long skip(long n) throws IOException { 224 if (n < 0) { 225 throw new IllegalArgumentException("negative skip length"); 226 } 227 ensureOpen(); 228 int max = (int)Math.min(n, Integer.MAX_VALUE); 229 int total = 0; 230 while (total < max) { 231 int len = max - total; 232 if (len > b.length) { 233 len = b.length; 234 } 235 len = read(b, 0, len); 236 if (len == -1) { 237 reachEOF = true; 238 break; 239 } 240 total += len; 241 } 242 return total; 243 } 244 245 /** 246 * Closes this input stream and releases any system resources associated 247 * with the stream. 248 * @exception IOException if an I/O error has occurred 249 */ close()250 public void close() throws IOException { 251 if (!closed) { 252 // Android-changed: Unconditionally close external inflaters (b/26462400) 253 //if (usesDefaultInflater) 254 inf.end(); 255 in.close(); 256 closed = true; 257 } 258 } 259 260 /** 261 * Fills input buffer with more data to decompress. 262 * @exception IOException if an I/O error has occurred 263 */ fill()264 protected void fill() throws IOException { 265 ensureOpen(); 266 len = in.read(buf, 0, buf.length); 267 if (len == -1) { 268 throw new EOFException("Unexpected end of ZLIB input stream"); 269 } 270 inf.setInput(buf, 0, len); 271 } 272 273 /** 274 * Tests if this input stream supports the <code>mark</code> and 275 * <code>reset</code> methods. The <code>markSupported</code> 276 * method of <code>InflaterInputStream</code> returns 277 * <code>false</code>. 278 * 279 * @return a <code>boolean</code> indicating if this stream type supports 280 * the <code>mark</code> and <code>reset</code> methods. 281 * @see java.io.InputStream#mark(int) 282 * @see java.io.InputStream#reset() 283 */ markSupported()284 public boolean markSupported() { 285 return false; 286 } 287 288 /** 289 * Marks the current position in this input stream. 290 * 291 * <p> The <code>mark</code> method of <code>InflaterInputStream</code> 292 * does nothing. 293 * 294 * @param readlimit the maximum limit of bytes that can be read before 295 * the mark position becomes invalid. 296 * @see java.io.InputStream#reset() 297 */ mark(int readlimit)298 public synchronized void mark(int readlimit) { 299 } 300 301 /** 302 * Repositions this stream to the position at the time the 303 * <code>mark</code> method was last called on this input stream. 304 * 305 * <p> The method <code>reset</code> for class 306 * <code>InflaterInputStream</code> does nothing except throw an 307 * <code>IOException</code>. 308 * 309 * @exception IOException if this method is invoked. 310 * @see java.io.InputStream#mark(int) 311 * @see java.io.IOException 312 */ reset()313 public synchronized void reset() throws IOException { 314 throw new IOException("mark/reset not supported"); 315 } 316 } 317