001/* InflaterInputStream.java - Input stream filter for decompressing
002   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.util.zip;
041
042import java.io.FilterInputStream;
043import java.io.IOException;
044import 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 */
057public 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}