001    /* InflaterInputStream.java - Input stream filter for decompressing
002       Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.util.zip;
041    
042    import java.io.FilterInputStream;
043    import java.io.IOException;
044    import java.io.InputStream;
045    
046    /**
047     * This filter stream is used to decompress data compressed in the "deflate"
048     * format. The "deflate" format is described in RFC 1951.
049     *
050     * This stream may form the basis for other decompression filters, such
051     * as the <code>GZIPInputStream</code>.
052     *
053     * @author John Leuner
054     * @author Tom Tromey
055     * @since 1.1
056     */
057    public class InflaterInputStream extends FilterInputStream
058    {
059      /**
060       * Decompressor for this filter
061       */
062      protected Inflater inf;
063    
064      /**
065       * Byte array used as a buffer
066       */
067      protected byte[] buf;
068    
069      /**
070       * Size of buffer
071       */
072      protected int len;
073    
074      // We just use this if we are decoding one byte at a time with the
075      // read() call.
076      private byte[] onebytebuffer = new byte[1];
077    
078      /**
079       * Create an InflaterInputStream with the default decompresseor
080       * and a default buffer size.
081       *
082       * @param in the InputStream to read bytes from
083       */
084      public InflaterInputStream(InputStream in)
085      {
086        this(in, new Inflater(), 4096);
087      }
088    
089      /**
090       * Create an InflaterInputStream with the specified decompresseor
091       * and a default buffer size.
092       *
093       * @param in the InputStream to read bytes from
094       * @param inf the decompressor used to decompress data read from in
095       */
096      public InflaterInputStream(InputStream in, Inflater inf)
097      {
098        this(in, inf, 4096);
099      }
100    
101      /**
102       * Create an InflaterInputStream with the specified decompresseor
103       * and a specified buffer size.
104       *
105       * @param in the InputStream to read bytes from
106       * @param inf the decompressor used to decompress data read from in
107       * @param size size of the buffer to use
108       */
109      public InflaterInputStream(InputStream in, Inflater inf, int size)
110      {
111        super(in);
112    
113        if (in == null)
114          throw new NullPointerException("in may not be null");
115        if (inf == null)
116          throw new NullPointerException("inf may not be null");
117        if (size < 0)
118          throw new IllegalArgumentException("size may not be negative");
119    
120        this.inf = inf;
121        this.buf = new byte [size];
122      }
123    
124      /**
125       * Returns 0 once the end of the stream (EOF) has been reached.
126       * Otherwise returns 1.
127       */
128      public int available() throws IOException
129      {
130        // According to the JDK 1.2 docs, this should only ever return 0
131        // or 1 and should not be relied upon by Java programs.
132        if (inf == null)
133          throw new IOException("stream closed");
134        return inf.finished() ? 0 : 1;
135      }
136    
137      /**
138       * Closes the input stream
139       */
140      public synchronized void close() throws IOException
141      {
142        if (in != null)
143          in.close();
144        in = null;
145      }
146    
147      /**
148       * Fills the buffer with more data to decompress.
149       */
150      protected void fill() throws IOException
151      {
152        if (in == null)
153          throw new ZipException ("InflaterInputStream is closed");
154    
155        len = in.read(buf, 0, buf.length);
156    
157        if (len < 0)
158          throw new ZipException("Deflated stream ends early.");
159    
160        inf.setInput(buf, 0, len);
161      }
162    
163      /**
164       * Reads one byte of decompressed data.
165       *
166       * The byte is in the lower 8 bits of the int.
167       */
168      public int read() throws IOException
169      {
170        int nread = read(onebytebuffer, 0, 1);
171        if (nread > 0)
172          return onebytebuffer[0] & 0xff;
173        return -1;
174      }
175    
176      /**
177       * Decompresses data into the byte array
178       *
179       * @param b the array to read and decompress data into
180       * @param off the offset indicating where the data should be placed
181       * @param len the number of bytes to decompress
182       */
183      public int read(byte[] b, int off, int len) throws IOException
184      {
185        if (inf == null)
186          throw new IOException("stream closed");
187        if (len == 0)
188          return 0;
189        if (inf.finished())
190          return -1;
191    
192        int count = 0;
193        while (count == 0)
194          {
195            if (inf.needsInput())
196              fill();
197    
198            try
199              {
200                count = inf.inflate(b, off, len);
201                if (count == 0)
202                  {
203                    if (this.len == -1)
204                      {
205                        // Couldn't get any more data to feed to the Inflater
206                        return -1;
207                      }
208                    if (inf.needsDictionary())
209                      throw new ZipException("Inflater needs Dictionary");
210                  }
211              }
212            catch (DataFormatException dfe)
213              {
214                throw new ZipException(dfe.getMessage());
215              }
216          }
217        return count;
218      }
219    
220      /**
221       * Skip specified number of bytes of uncompressed data
222       *
223       * @param n number of bytes to skip
224       */
225      public long skip(long n) throws IOException
226      {
227        if (inf == null)
228          throw new IOException("stream closed");
229        if (n < 0)
230          throw new IllegalArgumentException();
231    
232        if (n == 0)
233          return 0;
234    
235        int buflen = (int) Math.min(n, 2048);
236        byte[] tmpbuf = new byte[buflen];
237    
238        long skipped = 0L;
239        while (n > 0L)
240          {
241            int numread = read(tmpbuf, 0, buflen);
242            if (numread <= 0)
243              break;
244            n -= numread;
245            skipped += numread;
246            buflen = (int) Math.min(n, 2048);
247          }
248    
249        return skipped;
250     }
251    
252      public boolean markSupported()
253      {
254        return false;
255      }
256    
257      public void mark(int readLimit)
258      {
259      }
260    
261      public void reset() throws IOException
262      {
263        throw new IOException("reset not supported");
264      }
265    }