1   /*
2    * %W% %E%
3    *
4    * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6    */
7   
8   package java.util.zip;
9   
10  import java.io.FilterInputStream;
11  import java.io.InputStream;
12  import java.io.IOException;
13  import java.io.EOFException;
14  
15  /**
16   * This class implements a stream filter for uncompressing data in the
17   * "deflate" compression format. It is also used as the basis for other
18   * decompression filters, such as GZIPInputStream.
19   *
20   * @see     Inflater
21   * @version     %I%, %G%
22   * @author  David Connelly
23   */
24  public
25  class InflaterInputStream extends FilterInputStream {
26      /**
27       * Decompressor for this stream.
28       */
29      protected Inflater inf;
30  
31      /**
32       * Input buffer for decompression.
33       */
34      protected byte[] buf;
35  
36      /**
37       * Length of input buffer.
38       */
39      protected int len;
40  
41      private boolean closed = false;
42      // this flag is set to true after EOF has reached
43      private boolean reachEOF = false;
44      
45      /**
46       * Check to make sure that this stream has not been closed
47       */
48      private void ensureOpen() throws IOException {
49      if (closed) {
50          throw new IOException("Stream closed");
51          }
52      }
53  
54  
55      /**
56       * Creates a new input stream with the specified decompressor and
57       * buffer size.
58       * @param in the input stream
59       * @param inf the decompressor ("inflater")
60       * @param size the input buffer size
61       * @exception IllegalArgumentException if size is <= 0
62       */
63      public InflaterInputStream(InputStream in, Inflater inf, int size) {
64      super(in);
65          if (in == null || inf == null) {
66              throw new NullPointerException();
67          } else if (size <= 0) {
68              throw new IllegalArgumentException("buffer size <= 0");
69          }
70      this.inf = inf;
71      buf = new byte[size];
72      }
73  
74      /**
75       * Creates a new input stream with the specified decompressor and a
76       * default buffer size.
77       * @param in the input stream
78       * @param inf the decompressor ("inflater")
79       */
80      public InflaterInputStream(InputStream in, Inflater inf) {
81      this(in, inf, 512);
82      }
83  
84      boolean usesDefaultInflater = false;
85  
86      /**
87       * Creates a new input stream with a default decompressor and buffer size.
88       * @param in the input stream
89       */
90      public InflaterInputStream(InputStream in) {
91      this(in, new Inflater());
92          usesDefaultInflater = true;
93      }
94  
95      private byte[] singleByteBuf = new byte[1];
96  
97      /**
98       * Reads a byte of uncompressed data. This method will block until
99       * enough input is available for decompression.
100      * @return the byte read, or -1 if end of compressed input is reached
101      * @exception IOException if an I/O error has occurred
102      */
103     public int read() throws IOException {
104     ensureOpen();
105     return read(singleByteBuf, 0, 1) == -1 ? -1 : singleByteBuf[0] & 0xff;
106     }
107 
108     /**
109      * Reads uncompressed data into an array of bytes. If <code>len</code> is not
110      * zero, the method will block until some input can be decompressed; otherwise,
111      * no bytes are read and <code>0</code> is returned.
112      * @param b the buffer into which the data is read
113      * @param off the start offset in the destination array <code>b</code>
114      * @param len the maximum number of bytes read
115      * @return the actual number of bytes read, or -1 if the end of the
116      *         compressed input is reached or a preset dictionary is needed
117      * @exception  NullPointerException If <code>b</code> is <code>null</code>.
118      * @exception  IndexOutOfBoundsException If <code>off</code> is negative, 
119      * <code>len</code> is negative, or <code>len</code> is greater than 
120      * <code>b.length - off</code>
121      * @exception ZipException if a ZIP format error has occurred
122      * @exception IOException if an I/O error has occurred
123      */
124     public int read(byte[] b, int off, int len) throws IOException {
125     ensureOpen();
126     if (b == null) {
127         throw new NullPointerException();
128     } else if (off < 0 || len < 0 || len > b.length - off) {
129         throw new IndexOutOfBoundsException();
130     } else if (len == 0) {
131         return 0;
132     }
133     try {
134         int n;
135         while ((n = inf.inflate(b, off, len)) == 0) {
136         if (inf.finished() || inf.needsDictionary()) {
137                     reachEOF = true;
138             return -1;
139         }
140         if (inf.needsInput()) {
141             fill();
142         }
143         }
144         return n;
145     } catch (DataFormatException e) {
146         String s = e.getMessage();
147         throw new ZipException(s != null ? s : "Invalid ZLIB data format");
148     }
149     }
150 
151     /**
152      * Returns 0 after EOF has been reached, otherwise always return 1.
153      * <p>
154      * Programs should not count on this method to return the actual number
155      * of bytes that could be read without blocking.
156      *
157      * @return     1 before EOF and 0 after EOF.
158      * @exception  IOException  if an I/O error occurs.
159      * 
160      */
161     public int available() throws IOException {
162         ensureOpen();
163         if (reachEOF) {
164             return 0;
165         } else {
166             return 1;
167         }
168     }
169 
170     private byte[] b = new byte[512];
171 
172     /**
173      * Skips specified number of bytes of uncompressed data.
174      * @param n the number of bytes to skip
175      * @return the actual number of bytes skipped.
176      * @exception IOException if an I/O error has occurred
177      * @exception IllegalArgumentException if n < 0
178      */
179     public long skip(long n) throws IOException {
180         if (n < 0) {
181             throw new IllegalArgumentException("negative skip length");
182         }
183     ensureOpen();
184     int max = (int)Math.min(n, Integer.MAX_VALUE);
185     int total = 0;
186     while (total < max) {
187         int len = max - total;
188         if (len > b.length) {
189         len = b.length;
190         }
191         len = read(b, 0, len);
192         if (len == -1) {
193                 reachEOF = true;
194         break;
195         }
196         total += len;
197     }
198     return total;
199     }
200 
201     /**
202      * Closes this input stream and releases any system resources associated
203      * with the stream.
204      * @exception IOException if an I/O error has occurred
205      */
206     public void close() throws IOException {
207         if (!closed) {
208             if (usesDefaultInflater)
209                 inf.end();
210         in.close();
211             closed = true;
212         }
213     }
214 
215     /**
216      * Fills input buffer with more data to decompress.
217      * @exception IOException if an I/O error has occurred
218      */
219     protected void fill() throws IOException {
220     ensureOpen();
221     len = in.read(buf, 0, buf.length);
222     if (len == -1) {
223         throw new EOFException("Unexpected end of ZLIB input stream");
224     }
225     inf.setInput(buf, 0, len);
226     }
227 
228     /**
229      * Tests if this input stream supports the <code>mark</code> and
230      * <code>reset</code> methods. The <code>markSupported</code>
231      * method of <code>InflaterInputStream</code> returns
232      * <code>false</code>.
233      *
234      * @return  a <code>boolean</code> indicating if this stream type supports
235      *          the <code>mark</code> and <code>reset</code> methods.
236      * @see     java.io.InputStream#mark(int)
237      * @see     java.io.InputStream#reset()
238      */
239     public boolean markSupported() {
240         return false;
241     }
242  
243     /**
244      * Marks the current position in this input stream.
245      *
246      * <p> The <code>mark</code> method of <code>InflaterInputStream</code>
247      * does nothing.
248      *
249      * @param   readlimit   the maximum limit of bytes that can be read before
250      *                      the mark position becomes invalid.
251      * @see     java.io.InputStream#reset()
252      */
253     public synchronized void mark(int readlimit) {
254     }
255  
256     /**
257      * Repositions this stream to the position at the time the
258      * <code>mark</code> method was last called on this input stream.
259      *
260      * <p> The method <code>reset</code> for class
261      * <code>InflaterInputStream</code> does nothing except throw an
262      * <code>IOException</code>.
263      *
264      * @exception  IOException  if this method is invoked.
265      * @see     java.io.InputStream#mark(int)
266      * @see     java.io.IOException
267      */
268     public synchronized void reset() throws IOException {
269         throw new IOException("mark/reset not supported");
270     }
271 }
272