emul/mini/src/main/java/java/util/zip/Inflater.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 06 Feb 2013 15:47:06 +0100
branchemul
changeset 687 a9e506a27b55
parent 640 693745d01b55
child 694 0d277415ed02
permissions -rw-r--r--
Workaround of some problems with bytecode conversion demonstrated by ZipCompatibilityTest. Splitting the code into two new methods.
jaroslav@640
     1
/* Inflater.java - Decompress a data stream
jaroslav@640
     2
   Copyright (C) 1999, 2000, 2001, 2003  Free Software Foundation, Inc.
jaroslav@640
     3
jaroslav@640
     4
This file is part of GNU Classpath.
jaroslav@640
     5
jaroslav@640
     6
GNU Classpath is free software; you can redistribute it and/or modify
jaroslav@640
     7
it under the terms of the GNU General Public License as published by
jaroslav@640
     8
the Free Software Foundation; either version 2, or (at your option)
jaroslav@640
     9
any later version.
jaroslav@640
    10
 
jaroslav@640
    11
GNU Classpath is distributed in the hope that it will be useful, but
jaroslav@640
    12
WITHOUT ANY WARRANTY; without even the implied warranty of
jaroslav@640
    13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
jaroslav@640
    14
General Public License for more details.
jaroslav@640
    15
jaroslav@640
    16
You should have received a copy of the GNU General Public License
jaroslav@640
    17
along with GNU Classpath; see the file COPYING.  If not, write to the
jaroslav@640
    18
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
jaroslav@640
    19
02111-1307 USA.
jaroslav@640
    20
jaroslav@640
    21
Linking this library statically or dynamically with other modules is
jaroslav@640
    22
making a combined work based on this library.  Thus, the terms and
jaroslav@640
    23
conditions of the GNU General Public License cover the whole
jaroslav@640
    24
combination.
jaroslav@640
    25
jaroslav@640
    26
As a special exception, the copyright holders of this library give you
jaroslav@640
    27
permission to link this library with independent modules to produce an
jaroslav@640
    28
executable, regardless of the license terms of these independent
jaroslav@640
    29
modules, and to copy and distribute the resulting executable under
jaroslav@640
    30
terms of your choice, provided that you also meet, for each linked
jaroslav@640
    31
independent module, the terms and conditions of the license of that
jaroslav@640
    32
module.  An independent module is a module which is not derived from
jaroslav@640
    33
or based on this library.  If you modify this library, you may extend
jaroslav@640
    34
this exception to your version of the library, but you are not
jaroslav@640
    35
obligated to do so.  If you do not wish to do so, delete this
jaroslav@640
    36
exception statement from your version. */
jaroslav@609
    37
jaroslav@609
    38
package java.util.zip;
jaroslav@609
    39
jaroslav@640
    40
import org.apidesign.bck2brwsr.emul.lang.System;
jaroslav@611
    41
jaroslav@609
    42
/**
jaroslav@609
    43
 * This class provides support for general purpose decompression using the
jaroslav@609
    44
 * popular ZLIB compression library. The ZLIB compression library was
jaroslav@609
    45
 * initially developed as part of the PNG graphics standard and is not
jaroslav@609
    46
 * protected by patents. It is fully described in the specifications at
jaroslav@609
    47
 * the <a href="package-summary.html#package_description">java.util.zip
jaroslav@609
    48
 * package description</a>.
jaroslav@609
    49
 *
jaroslav@609
    50
 * <p>The following code fragment demonstrates a trivial compression
jaroslav@609
    51
 * and decompression of a string using <tt>Deflater</tt> and
jaroslav@609
    52
 * <tt>Inflater</tt>.
jaroslav@609
    53
 *
jaroslav@609
    54
 * <blockquote><pre>
jaroslav@609
    55
 * try {
jaroslav@609
    56
 *     // Encode a String into bytes
jaroslav@609
    57
 *     String inputString = "blahblahblah\u20AC\u20AC";
jaroslav@609
    58
 *     byte[] input = inputString.getBytes("UTF-8");
jaroslav@609
    59
 *
jaroslav@609
    60
 *     // Compress the bytes
jaroslav@609
    61
 *     byte[] output = new byte[100];
jaroslav@609
    62
 *     Deflater compresser = new Deflater();
jaroslav@609
    63
 *     compresser.setInput(input);
jaroslav@609
    64
 *     compresser.finish();
jaroslav@609
    65
 *     int compressedDataLength = compresser.deflate(output);
jaroslav@609
    66
 *
jaroslav@609
    67
 *     // Decompress the bytes
jaroslav@609
    68
 *     Inflater decompresser = new Inflater();
jaroslav@609
    69
 *     decompresser.setInput(output, 0, compressedDataLength);
jaroslav@609
    70
 *     byte[] result = new byte[100];
jaroslav@609
    71
 *     int resultLength = decompresser.inflate(result);
jaroslav@609
    72
 *     decompresser.end();
jaroslav@609
    73
 *
jaroslav@609
    74
 *     // Decode the bytes into a String
jaroslav@609
    75
 *     String outputString = new String(result, 0, resultLength, "UTF-8");
jaroslav@609
    76
 * } catch(java.io.UnsupportedEncodingException ex) {
jaroslav@609
    77
 *     // handle
jaroslav@609
    78
 * } catch (java.util.zip.DataFormatException ex) {
jaroslav@609
    79
 *     // handle
jaroslav@609
    80
 * }
jaroslav@609
    81
 * </pre></blockquote>
jaroslav@609
    82
 *
jaroslav@609
    83
 * @see         Deflater
jaroslav@609
    84
 * @author      David Connelly
jaroslav@609
    85
 *
jaroslav@609
    86
 */
jaroslav@609
    87
jaroslav@640
    88
/* Written using on-line Java Platform 1.2 API Specification
jaroslav@640
    89
 * and JCL book.
jaroslav@640
    90
 * Believed complete and correct.
jaroslav@640
    91
 */
jaroslav@609
    92
jaroslav@640
    93
/**
jaroslav@640
    94
 * Inflater is used to decompress data that has been compressed according 
jaroslav@640
    95
 * to the "deflate" standard described in rfc1950.
jaroslav@640
    96
 *
jaroslav@640
    97
 * The usage is as following.  First you have to set some input with
jaroslav@640
    98
 * <code>setInput()</code>, then inflate() it.  If inflate doesn't
jaroslav@640
    99
 * inflate any bytes there may be three reasons:
jaroslav@640
   100
 * <ul>
jaroslav@640
   101
 * <li>needsInput() returns true because the input buffer is empty.
jaroslav@640
   102
 * You have to provide more input with <code>setInput()</code>.  
jaroslav@640
   103
 * NOTE: needsInput() also returns true when, the stream is finished.
jaroslav@640
   104
 * </li>
jaroslav@640
   105
 * <li>needsDictionary() returns true, you have to provide a preset 
jaroslav@640
   106
 *     dictionary with <code>setDictionary()</code>.</li>
jaroslav@640
   107
 * <li>finished() returns true, the inflater has finished.</li>
jaroslav@640
   108
 * </ul>
jaroslav@640
   109
 * Once the first output byte is produced, a dictionary will not be
jaroslav@640
   110
 * needed at a later stage.
jaroslav@640
   111
 *
jaroslav@640
   112
 * @author John Leuner, Jochen Hoenicke
jaroslav@640
   113
 * @author Tom Tromey
jaroslav@640
   114
 * @date May 17, 1999
jaroslav@640
   115
 * @since JDK 1.1
jaroslav@640
   116
 */
jaroslav@640
   117
public class Inflater
jaroslav@640
   118
{
jaroslav@640
   119
  /* Copy lengths for literal codes 257..285 */
jaroslav@640
   120
  private static final int CPLENS[] = 
jaroslav@640
   121
  { 
jaroslav@640
   122
    3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
jaroslav@640
   123
    35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
jaroslav@640
   124
  };
jaroslav@640
   125
  
jaroslav@640
   126
  /* Extra bits for literal codes 257..285 */  
jaroslav@640
   127
  private static final int CPLEXT[] = 
jaroslav@640
   128
  { 
jaroslav@640
   129
    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
jaroslav@640
   130
    3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
jaroslav@640
   131
  };
jaroslav@640
   132
jaroslav@640
   133
  /* Copy offsets for distance codes 0..29 */
jaroslav@640
   134
  private static final int CPDIST[] = {
jaroslav@640
   135
    1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
jaroslav@640
   136
    257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
jaroslav@640
   137
    8193, 12289, 16385, 24577
jaroslav@640
   138
  };
jaroslav@640
   139
  
jaroslav@640
   140
  /* Extra bits for distance codes */
jaroslav@640
   141
  private static final int CPDEXT[] = {
jaroslav@640
   142
    0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
jaroslav@640
   143
    7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 
jaroslav@640
   144
    12, 12, 13, 13
jaroslav@640
   145
  };
jaroslav@640
   146
jaroslav@640
   147
  /* This are the state in which the inflater can be.  */
jaroslav@640
   148
  private static final int DECODE_HEADER           = 0;
jaroslav@640
   149
  private static final int DECODE_DICT             = 1;
jaroslav@640
   150
  private static final int DECODE_BLOCKS           = 2;
jaroslav@640
   151
  private static final int DECODE_STORED_LEN1      = 3;
jaroslav@640
   152
  private static final int DECODE_STORED_LEN2      = 4;
jaroslav@640
   153
  private static final int DECODE_STORED           = 5;
jaroslav@640
   154
  private static final int DECODE_DYN_HEADER       = 6;
jaroslav@640
   155
  private static final int DECODE_HUFFMAN          = 7;
jaroslav@640
   156
  private static final int DECODE_HUFFMAN_LENBITS  = 8;
jaroslav@640
   157
  private static final int DECODE_HUFFMAN_DIST     = 9;
jaroslav@640
   158
  private static final int DECODE_HUFFMAN_DISTBITS = 10;
jaroslav@640
   159
  private static final int DECODE_CHKSUM           = 11;
jaroslav@640
   160
  private static final int FINISHED                = 12;
jaroslav@640
   161
jaroslav@640
   162
  /** This variable contains the current state. */
jaroslav@640
   163
  private int mode;
jaroslav@640
   164
jaroslav@640
   165
  /**
jaroslav@640
   166
   * The adler checksum of the dictionary or of the decompressed
jaroslav@640
   167
   * stream, as it is written in the header resp. footer of the
jaroslav@640
   168
   * compressed stream.  <br>
jaroslav@640
   169
   *
jaroslav@640
   170
   * Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
jaroslav@640
   171
   */
jaroslav@640
   172
  private int readAdler;
jaroslav@640
   173
  /** 
jaroslav@640
   174
   * The number of bits needed to complete the current state.  This
jaroslav@640
   175
   * is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
jaroslav@640
   176
   * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.  
jaroslav@640
   177
   */
jaroslav@640
   178
  private int neededBits;
jaroslav@640
   179
  private int repLength, repDist;
jaroslav@640
   180
  private int uncomprLen;
jaroslav@640
   181
  /**
jaroslav@640
   182
   * True, if the last block flag was set in the last block of the
jaroslav@640
   183
   * inflated stream.  This means that the stream ends after the
jaroslav@640
   184
   * current block.  
jaroslav@640
   185
   */
jaroslav@640
   186
  private boolean isLastBlock;
jaroslav@640
   187
jaroslav@640
   188
  /**
jaroslav@640
   189
   * The total number of inflated bytes.
jaroslav@640
   190
   */
jaroslav@640
   191
  private long totalOut;
jaroslav@640
   192
  /**
jaroslav@640
   193
   * The total number of bytes set with setInput().  This is not the
jaroslav@640
   194
   * value returned by getTotalIn(), since this also includes the 
jaroslav@640
   195
   * unprocessed input.
jaroslav@640
   196
   */
jaroslav@640
   197
  private long totalIn;
jaroslav@640
   198
  /**
jaroslav@640
   199
   * This variable stores the nowrap flag that was given to the constructor.
jaroslav@640
   200
   * True means, that the inflated stream doesn't contain a header nor the
jaroslav@640
   201
   * checksum in the footer.
jaroslav@640
   202
   */
jaroslav@640
   203
  private boolean nowrap;
jaroslav@640
   204
jaroslav@640
   205
  private StreamManipulator input;
jaroslav@640
   206
  private OutputWindow outputWindow;
jaroslav@640
   207
  private InflaterDynHeader dynHeader;
jaroslav@640
   208
  private InflaterHuffmanTree litlenTree, distTree;
jaroslav@640
   209
  private Adler32 adler;
jaroslav@640
   210
jaroslav@640
   211
  /**
jaroslav@640
   212
   * Creates a new inflater.
jaroslav@640
   213
   */
jaroslav@640
   214
  public Inflater ()
jaroslav@640
   215
  {
jaroslav@640
   216
    this (false);
jaroslav@640
   217
  }
jaroslav@640
   218
jaroslav@640
   219
  /**
jaroslav@640
   220
   * Creates a new inflater.
jaroslav@640
   221
   * @param nowrap true if no header and checksum field appears in the
jaroslav@640
   222
   * stream.  This is used for GZIPed input.  For compatibility with
jaroslav@640
   223
   * Sun JDK you should provide one byte of input more than needed in
jaroslav@640
   224
   * this case.
jaroslav@640
   225
   */
jaroslav@640
   226
  public Inflater (boolean nowrap)
jaroslav@640
   227
  {
jaroslav@640
   228
    this.nowrap = nowrap;
jaroslav@640
   229
    this.adler = new Adler32();
jaroslav@640
   230
    input = new StreamManipulator();
jaroslav@640
   231
    outputWindow = new OutputWindow();
jaroslav@640
   232
    mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
jaroslav@640
   233
  }
jaroslav@640
   234
jaroslav@640
   235
  /**
jaroslav@640
   236
   * Finalizes this object.
jaroslav@640
   237
   */
jaroslav@640
   238
  protected void finalize ()
jaroslav@640
   239
  {
jaroslav@640
   240
    /* Exists only for compatibility */
jaroslav@640
   241
  }
jaroslav@640
   242
jaroslav@640
   243
  /**
jaroslav@640
   244
   * Frees all objects allocated by the inflater.  There's no reason
jaroslav@640
   245
   * to call this, since you can just rely on garbage collection (even
jaroslav@640
   246
   * for the Sun implementation).  Exists only for compatibility
jaroslav@640
   247
   * with Sun's JDK, where the compressor allocates native memory.
jaroslav@640
   248
   * If you call any method (even reset) afterwards the behaviour is
jaroslav@640
   249
   * <i>undefined</i>.  
jaroslav@640
   250
   * @deprecated Just clear all references to inflater instead.
jaroslav@640
   251
   */
jaroslav@640
   252
  public void end ()
jaroslav@640
   253
  {
jaroslav@640
   254
    outputWindow = null;
jaroslav@640
   255
    input = null;
jaroslav@640
   256
    dynHeader = null;
jaroslav@640
   257
    litlenTree = null;
jaroslav@640
   258
    distTree = null;
jaroslav@640
   259
    adler = null;
jaroslav@640
   260
  }
jaroslav@640
   261
jaroslav@640
   262
  /**
jaroslav@640
   263
   * Returns true, if the inflater has finished.  This means, that no
jaroslav@640
   264
   * input is needed and no output can be produced.
jaroslav@640
   265
   */
jaroslav@640
   266
  public boolean finished() 
jaroslav@640
   267
  {
jaroslav@640
   268
    return mode == FINISHED && outputWindow.getAvailable() == 0;
jaroslav@640
   269
  }
jaroslav@640
   270
jaroslav@640
   271
  /**
jaroslav@640
   272
   * Gets the adler checksum.  This is either the checksum of all
jaroslav@640
   273
   * uncompressed bytes returned by inflate(), or if needsDictionary()
jaroslav@640
   274
   * returns true (and thus no output was yet produced) this is the
jaroslav@640
   275
   * adler checksum of the expected dictionary.
jaroslav@640
   276
   * @returns the adler checksum.
jaroslav@640
   277
   */
jaroslav@640
   278
  public int getAdler()
jaroslav@640
   279
  {
jaroslav@640
   280
    return needsDictionary() ? readAdler : (int) adler.getValue();
jaroslav@640
   281
  }
jaroslav@640
   282
  
jaroslav@640
   283
  /**
jaroslav@640
   284
   * Gets the number of unprocessed input.  Useful, if the end of the
jaroslav@640
   285
   * stream is reached and you want to further process the bytes after
jaroslav@640
   286
   * the deflate stream.  
jaroslav@640
   287
   * @return the number of bytes of the input which were not processed.
jaroslav@640
   288
   */
jaroslav@640
   289
  public int getRemaining()
jaroslav@640
   290
  {
jaroslav@640
   291
    return input.getAvailableBytes();
jaroslav@640
   292
  }
jaroslav@640
   293
  
jaroslav@640
   294
  /**
jaroslav@640
   295
   * Gets the total number of processed compressed input bytes.
jaroslav@640
   296
   * @return the total number of bytes of processed input bytes.
jaroslav@640
   297
   */
jaroslav@640
   298
  public int getTotalIn()
jaroslav@640
   299
  {
jaroslav@640
   300
    return (int)getBytesRead();
jaroslav@640
   301
  }
jaroslav@640
   302
  
jaroslav@640
   303
  /**
jaroslav@640
   304
   * Gets the total number of output bytes returned by inflate().
jaroslav@640
   305
   * @return the total number of output bytes.
jaroslav@640
   306
   */
jaroslav@640
   307
  public int getTotalOut()
jaroslav@640
   308
  {
jaroslav@640
   309
    return (int)totalOut;
jaroslav@640
   310
  }
jaroslav@640
   311
  
jaroslav@640
   312
  public long getBytesWritten() {
jaroslav@640
   313
     return totalOut;
jaroslav@640
   314
  }
jaroslav@640
   315
jaroslav@640
   316
  public long getBytesRead() {
jaroslav@640
   317
    return totalIn - getRemaining();
jaroslav@640
   318
  }
jaroslav@640
   319
  
jaroslav@640
   320
jaroslav@640
   321
  /**
jaroslav@640
   322
   * Inflates the compressed stream to the output buffer.  If this
jaroslav@640
   323
   * returns 0, you should check, whether needsDictionary(),
jaroslav@640
   324
   * needsInput() or finished() returns true, to determine why no 
jaroslav@640
   325
   * further output is produced.
jaroslav@640
   326
   * @param buffer the output buffer.
jaroslav@640
   327
   * @return the number of bytes written to the buffer, 0 if no further
jaroslav@640
   328
   * output can be produced.  
jaroslav@640
   329
   * @exception DataFormatException if deflated stream is invalid.
jaroslav@640
   330
   * @exception IllegalArgumentException if buf has length 0.
jaroslav@640
   331
   */
jaroslav@640
   332
  public int inflate (byte[] buf) throws DataFormatException
jaroslav@640
   333
  {
jaroslav@640
   334
    return inflate (buf, 0, buf.length);
jaroslav@640
   335
  }
jaroslav@640
   336
jaroslav@640
   337
  /**
jaroslav@640
   338
   * Inflates the compressed stream to the output buffer.  If this
jaroslav@640
   339
   * returns 0, you should check, whether needsDictionary(),
jaroslav@640
   340
   * needsInput() or finished() returns true, to determine why no 
jaroslav@640
   341
   * further output is produced.
jaroslav@640
   342
   * @param buffer the output buffer.
jaroslav@640
   343
   * @param off the offset into buffer where the output should start.
jaroslav@640
   344
   * @param len the maximum length of the output.
jaroslav@640
   345
   * @return the number of bytes written to the buffer, 0 if no further
jaroslav@640
   346
   * output can be produced.  
jaroslav@640
   347
   * @exception DataFormatException if deflated stream is invalid.
jaroslav@640
   348
   * @exception IndexOutOfBoundsException if the off and/or len are wrong.
jaroslav@640
   349
   */
jaroslav@640
   350
  public int inflate (byte[] buf, int off, int len) throws DataFormatException
jaroslav@640
   351
  {
jaroslav@640
   352
    /* Special case: len may be zero */
jaroslav@640
   353
    if (len == 0)
jaroslav@640
   354
      return 0;
jaroslav@640
   355
    /* Check for correct buff, off, len triple */
jaroslav@640
   356
    if (0 > off || off > off + len || off + len > buf.length)
jaroslav@640
   357
      throw new ArrayIndexOutOfBoundsException();
jaroslav@640
   358
    int count = 0;
jaroslav@640
   359
    int more;
jaroslav@640
   360
    do
jaroslav@640
   361
      {
jaroslav@640
   362
	if (mode != DECODE_CHKSUM)
jaroslav@640
   363
	  {
jaroslav@640
   364
	    /* Don't give away any output, if we are waiting for the
jaroslav@640
   365
	     * checksum in the input stream.
jaroslav@640
   366
	     *
jaroslav@640
   367
	     * With this trick we have always:
jaroslav@640
   368
	     *   needsInput() and not finished() 
jaroslav@640
   369
	     *   implies more output can be produced.  
jaroslav@640
   370
	     */
jaroslav@640
   371
	    more = outputWindow.copyOutput(buf, off, len);
jaroslav@640
   372
	    adler.update(buf, off, more);
jaroslav@640
   373
	    off += more;
jaroslav@640
   374
	    count += more;
jaroslav@640
   375
	    totalOut += more;
jaroslav@640
   376
	    len -= more;
jaroslav@640
   377
	    if (len == 0)
jaroslav@640
   378
	      return count;
jaroslav@640
   379
	  }
jaroslav@640
   380
      }
jaroslav@640
   381
    while (decode() || (outputWindow.getAvailable() > 0
jaroslav@640
   382
			&& mode != DECODE_CHKSUM));
jaroslav@640
   383
    return count;
jaroslav@640
   384
  }
jaroslav@640
   385
jaroslav@640
   386
  /**
jaroslav@640
   387
   * Returns true, if a preset dictionary is needed to inflate the input.
jaroslav@640
   388
   */
jaroslav@640
   389
  public boolean needsDictionary ()
jaroslav@640
   390
  {
jaroslav@640
   391
    return mode == DECODE_DICT && neededBits == 0;
jaroslav@640
   392
  }
jaroslav@640
   393
jaroslav@640
   394
  /**
jaroslav@640
   395
   * Returns true, if the input buffer is empty.
jaroslav@640
   396
   * You should then call setInput(). <br>
jaroslav@640
   397
   *
jaroslav@640
   398
   * <em>NOTE</em>: This method also returns true when the stream is finished.
jaroslav@640
   399
   */
jaroslav@640
   400
  public boolean needsInput () 
jaroslav@640
   401
  {
jaroslav@640
   402
    return input.needsInput ();
jaroslav@640
   403
  }
jaroslav@640
   404
jaroslav@640
   405
  /**
jaroslav@640
   406
   * Resets the inflater so that a new stream can be decompressed.  All
jaroslav@640
   407
   * pending input and output will be discarded.
jaroslav@640
   408
   */
jaroslav@640
   409
  public void reset ()
jaroslav@640
   410
  {
jaroslav@640
   411
    mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
jaroslav@640
   412
    totalIn = totalOut = 0;
jaroslav@640
   413
    input.reset();
jaroslav@640
   414
    outputWindow.reset();
jaroslav@640
   415
    dynHeader = null;
jaroslav@640
   416
    litlenTree = null;
jaroslav@640
   417
    distTree = null;
jaroslav@640
   418
    isLastBlock = false;
jaroslav@640
   419
    adler.reset();
jaroslav@640
   420
  }
jaroslav@640
   421
jaroslav@640
   422
  /**
jaroslav@640
   423
   * Sets the preset dictionary.  This should only be called, if
jaroslav@640
   424
   * needsDictionary() returns true and it should set the same
jaroslav@640
   425
   * dictionary, that was used for deflating.  The getAdler()
jaroslav@640
   426
   * function returns the checksum of the dictionary needed.
jaroslav@640
   427
   * @param buffer the dictionary.
jaroslav@640
   428
   * @exception IllegalStateException if no dictionary is needed.
jaroslav@640
   429
   * @exception IllegalArgumentException if the dictionary checksum is
jaroslav@640
   430
   * wrong.  
jaroslav@640
   431
   */
jaroslav@640
   432
  public void setDictionary (byte[] buffer)
jaroslav@640
   433
  {
jaroslav@640
   434
    setDictionary(buffer, 0, buffer.length);
jaroslav@640
   435
  }
jaroslav@640
   436
jaroslav@640
   437
  /**
jaroslav@640
   438
   * Sets the preset dictionary.  This should only be called, if
jaroslav@640
   439
   * needsDictionary() returns true and it should set the same
jaroslav@640
   440
   * dictionary, that was used for deflating.  The getAdler()
jaroslav@640
   441
   * function returns the checksum of the dictionary needed.
jaroslav@640
   442
   * @param buffer the dictionary.
jaroslav@640
   443
   * @param off the offset into buffer where the dictionary starts.
jaroslav@640
   444
   * @param len the length of the dictionary.
jaroslav@640
   445
   * @exception IllegalStateException if no dictionary is needed.
jaroslav@640
   446
   * @exception IllegalArgumentException if the dictionary checksum is
jaroslav@640
   447
   * wrong.  
jaroslav@640
   448
   * @exception IndexOutOfBoundsException if the off and/or len are wrong.
jaroslav@640
   449
   */
jaroslav@640
   450
  public void setDictionary (byte[] buffer, int off, int len)
jaroslav@640
   451
  {
jaroslav@640
   452
    if (!needsDictionary())
jaroslav@640
   453
      throw new IllegalStateException();
jaroslav@640
   454
jaroslav@640
   455
    adler.update(buffer, off, len);
jaroslav@640
   456
    if ((int) adler.getValue() != readAdler)
jaroslav@640
   457
      throw new IllegalArgumentException("Wrong adler checksum");
jaroslav@640
   458
    adler.reset();
jaroslav@640
   459
    outputWindow.copyDict(buffer, off, len);
jaroslav@640
   460
    mode = DECODE_BLOCKS;
jaroslav@640
   461
  }
jaroslav@640
   462
jaroslav@640
   463
  /**
jaroslav@640
   464
   * Sets the input.  This should only be called, if needsInput()
jaroslav@640
   465
   * returns true.
jaroslav@640
   466
   * @param buffer the input.
jaroslav@640
   467
   * @exception IllegalStateException if no input is needed.
jaroslav@640
   468
   */
jaroslav@640
   469
  public void setInput (byte[] buf) 
jaroslav@640
   470
  {
jaroslav@640
   471
    setInput (buf, 0, buf.length);
jaroslav@640
   472
  }
jaroslav@640
   473
jaroslav@640
   474
  /**
jaroslav@640
   475
   * Sets the input.  This should only be called, if needsInput()
jaroslav@640
   476
   * returns true.
jaroslav@640
   477
   * @param buffer the input.
jaroslav@640
   478
   * @param off the offset into buffer where the input starts.
jaroslav@640
   479
   * @param len the length of the input.  
jaroslav@640
   480
   * @exception IllegalStateException if no input is needed.
jaroslav@640
   481
   * @exception IndexOutOfBoundsException if the off and/or len are wrong.
jaroslav@640
   482
   */
jaroslav@640
   483
  public void setInput (byte[] buf, int off, int len) 
jaroslav@640
   484
  {
jaroslav@640
   485
    input.setInput (buf, off, len);
jaroslav@640
   486
    totalIn += len;
jaroslav@640
   487
  }
jaroslav@640
   488
  private static final int DEFLATED = 8;
jaroslav@640
   489
  /**
jaroslav@640
   490
   * Decodes the deflate header.
jaroslav@640
   491
   * @return false if more input is needed. 
jaroslav@640
   492
   * @exception DataFormatException if header is invalid.
jaroslav@640
   493
   */
jaroslav@640
   494
  private boolean decodeHeader () throws DataFormatException
jaroslav@640
   495
  {
jaroslav@640
   496
    int header = input.peekBits(16);
jaroslav@640
   497
    if (header < 0)
jaroslav@640
   498
      return false;
jaroslav@640
   499
    input.dropBits(16);
jaroslav@640
   500
    
jaroslav@640
   501
    /* The header is written in "wrong" byte order */
jaroslav@640
   502
    header = ((header << 8) | (header >> 8)) & 0xffff;
jaroslav@640
   503
    if (header % 31 != 0)
jaroslav@640
   504
      throw new DataFormatException("Header checksum illegal");
jaroslav@640
   505
    
jaroslav@640
   506
    if ((header & 0x0f00) != (DEFLATED << 8))
jaroslav@640
   507
      throw new DataFormatException("Compression Method unknown");
jaroslav@640
   508
jaroslav@640
   509
    /* Maximum size of the backwards window in bits. 
jaroslav@640
   510
     * We currently ignore this, but we could use it to make the
jaroslav@640
   511
     * inflater window more space efficient. On the other hand the
jaroslav@640
   512
     * full window (15 bits) is needed most times, anyway.
jaroslav@640
   513
     int max_wbits = ((header & 0x7000) >> 12) + 8;
jaroslav@640
   514
     */
jaroslav@640
   515
    
jaroslav@640
   516
    if ((header & 0x0020) == 0) // Dictionary flag?
jaroslav@640
   517
      {
jaroslav@640
   518
	mode = DECODE_BLOCKS;
jaroslav@640
   519
      }
jaroslav@640
   520
    else
jaroslav@640
   521
      {
jaroslav@640
   522
	mode = DECODE_DICT;
jaroslav@640
   523
	neededBits = 32;      
jaroslav@640
   524
      }
jaroslav@640
   525
    return true;
jaroslav@640
   526
  }
jaroslav@640
   527
   
jaroslav@640
   528
  /**
jaroslav@640
   529
   * Decodes the dictionary checksum after the deflate header.
jaroslav@640
   530
   * @return false if more input is needed. 
jaroslav@640
   531
   */
jaroslav@640
   532
  private boolean decodeDict ()
jaroslav@640
   533
  {
jaroslav@640
   534
    while (neededBits > 0)
jaroslav@640
   535
      {
jaroslav@640
   536
	int dictByte = input.peekBits(8);
jaroslav@640
   537
	if (dictByte < 0)
jaroslav@640
   538
	  return false;
jaroslav@640
   539
	input.dropBits(8);
jaroslav@640
   540
	readAdler = (readAdler << 8) | dictByte;
jaroslav@640
   541
	neededBits -= 8;
jaroslav@640
   542
      }
jaroslav@640
   543
    return false;
jaroslav@640
   544
  }
jaroslav@640
   545
jaroslav@640
   546
  /**
jaroslav@640
   547
   * Decodes the huffman encoded symbols in the input stream.
jaroslav@640
   548
   * @return false if more input is needed, true if output window is
jaroslav@640
   549
   * full or the current block ends.
jaroslav@640
   550
   * @exception DataFormatException if deflated stream is invalid.  
jaroslav@640
   551
   */
jaroslav@640
   552
  private boolean decodeHuffman () throws DataFormatException
jaroslav@640
   553
  {
jaroslav@640
   554
    int free = outputWindow.getFreeSpace();
jaroslav@640
   555
    while (free >= 258)
jaroslav@640
   556
      {
jaroslav@640
   557
	int symbol;
jaroslav@640
   558
	switch (mode)
jaroslav@640
   559
	  {
jaroslav@640
   560
	  case DECODE_HUFFMAN:
jaroslav@640
   561
	    /* This is the inner loop so it is optimized a bit */
jaroslav@640
   562
	    while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0)
jaroslav@640
   563
	      {
jaroslav@640
   564
		outputWindow.write(symbol);
jaroslav@640
   565
		if (--free < 258)
jaroslav@640
   566
		  return true;
jaroslav@640
   567
	      } 
jaroslav@640
   568
	    if (symbol < 257)
jaroslav@640
   569
	      {
jaroslav@640
   570
		if (symbol < 0)
jaroslav@640
   571
		  return false;
jaroslav@640
   572
		else
jaroslav@640
   573
		  {
jaroslav@640
   574
		    /* symbol == 256: end of block */
jaroslav@640
   575
		    distTree = null;
jaroslav@640
   576
		    litlenTree = null;
jaroslav@640
   577
		    mode = DECODE_BLOCKS;
jaroslav@640
   578
		    return true;
jaroslav@640
   579
		  }
jaroslav@640
   580
	      }
jaroslav@640
   581
		
jaroslav@640
   582
	    try
jaroslav@640
   583
	      {
jaroslav@640
   584
		repLength = CPLENS[symbol - 257];
jaroslav@640
   585
		neededBits = CPLEXT[symbol - 257];
jaroslav@640
   586
	      }
jaroslav@640
   587
	    catch (ArrayIndexOutOfBoundsException ex)
jaroslav@640
   588
	      {
jaroslav@640
   589
		throw new DataFormatException("Illegal rep length code");
jaroslav@640
   590
	      }
jaroslav@640
   591
	    /* fall through */
jaroslav@640
   592
	  case DECODE_HUFFMAN_LENBITS:
jaroslav@640
   593
	    if (neededBits > 0)
jaroslav@640
   594
	      {
jaroslav@640
   595
		mode = DECODE_HUFFMAN_LENBITS;
jaroslav@640
   596
		int i = input.peekBits(neededBits);
jaroslav@640
   597
		if (i < 0)
jaroslav@640
   598
		  return false;
jaroslav@640
   599
		input.dropBits(neededBits);
jaroslav@640
   600
		repLength += i;
jaroslav@640
   601
	      }
jaroslav@640
   602
	    mode = DECODE_HUFFMAN_DIST;
jaroslav@640
   603
	    /* fall through */
jaroslav@640
   604
	  case DECODE_HUFFMAN_DIST:
jaroslav@640
   605
	    symbol = distTree.getSymbol(input);
jaroslav@640
   606
	    if (symbol < 0)
jaroslav@640
   607
	      return false;
jaroslav@640
   608
	    try 
jaroslav@640
   609
	      {
jaroslav@640
   610
		repDist = CPDIST[symbol];
jaroslav@640
   611
		neededBits = CPDEXT[symbol];
jaroslav@640
   612
	      }
jaroslav@640
   613
	    catch (ArrayIndexOutOfBoundsException ex)
jaroslav@640
   614
	      {
jaroslav@640
   615
		throw new DataFormatException("Illegal rep dist code");
jaroslav@640
   616
	      }
jaroslav@640
   617
	    /* fall through */
jaroslav@640
   618
	  case DECODE_HUFFMAN_DISTBITS:
jaroslav@640
   619
	    if (neededBits > 0)
jaroslav@640
   620
	      {
jaroslav@640
   621
		mode = DECODE_HUFFMAN_DISTBITS;
jaroslav@640
   622
		int i = input.peekBits(neededBits);
jaroslav@640
   623
		if (i < 0)
jaroslav@640
   624
		  return false;
jaroslav@640
   625
		input.dropBits(neededBits);
jaroslav@640
   626
		repDist += i;
jaroslav@640
   627
	      }
jaroslav@640
   628
	    outputWindow.repeat(repLength, repDist);
jaroslav@640
   629
	    free -= repLength;
jaroslav@640
   630
	    mode = DECODE_HUFFMAN;
jaroslav@640
   631
	    break;
jaroslav@640
   632
	  default:
jaroslav@640
   633
	    throw new IllegalStateException();
jaroslav@640
   634
	  }
jaroslav@640
   635
      }
jaroslav@640
   636
    return true;
jaroslav@640
   637
  }
jaroslav@640
   638
jaroslav@640
   639
  /**
jaroslav@640
   640
   * Decodes the adler checksum after the deflate stream.
jaroslav@640
   641
   * @return false if more input is needed. 
jaroslav@640
   642
   * @exception DataFormatException if checksum doesn't match.
jaroslav@640
   643
   */
jaroslav@640
   644
  private boolean decodeChksum () throws DataFormatException
jaroslav@640
   645
  {
jaroslav@640
   646
    while (neededBits > 0)
jaroslav@640
   647
      {
jaroslav@640
   648
	int chkByte = input.peekBits(8);
jaroslav@640
   649
	if (chkByte < 0)
jaroslav@640
   650
	  return false;
jaroslav@640
   651
	input.dropBits(8);
jaroslav@640
   652
	readAdler = (readAdler << 8) | chkByte;
jaroslav@640
   653
	neededBits -= 8;
jaroslav@640
   654
      }
jaroslav@640
   655
    if ((int) adler.getValue() != readAdler)
jaroslav@640
   656
      throw new DataFormatException("Adler chksum doesn't match: "
jaroslav@640
   657
				    +Integer.toHexString((int)adler.getValue())
jaroslav@640
   658
				    +" vs. "+Integer.toHexString(readAdler));
jaroslav@640
   659
    mode = FINISHED;
jaroslav@640
   660
    return false;
jaroslav@640
   661
  }
jaroslav@640
   662
jaroslav@640
   663
  /**
jaroslav@640
   664
   * Decodes the deflated stream.
jaroslav@640
   665
   * @return false if more input is needed, or if finished. 
jaroslav@640
   666
   * @exception DataFormatException if deflated stream is invalid.
jaroslav@640
   667
   */
jaroslav@640
   668
  private boolean decode () throws DataFormatException
jaroslav@640
   669
  {
jaroslav@640
   670
    switch (mode) 
jaroslav@640
   671
      {
jaroslav@640
   672
      case DECODE_HEADER:
jaroslav@640
   673
	return decodeHeader();
jaroslav@640
   674
      case DECODE_DICT:
jaroslav@640
   675
	return decodeDict();
jaroslav@640
   676
      case DECODE_CHKSUM:
jaroslav@640
   677
	return decodeChksum();
jaroslav@640
   678
jaroslav@640
   679
      case DECODE_BLOCKS:
jaroslav@640
   680
	if (isLastBlock)
jaroslav@640
   681
	  {
jaroslav@640
   682
	    if (nowrap)
jaroslav@640
   683
	      {
jaroslav@640
   684
		mode = FINISHED;
jaroslav@640
   685
		return false;
jaroslav@640
   686
	      }
jaroslav@640
   687
	    else
jaroslav@640
   688
	      {
jaroslav@640
   689
		input.skipToByteBoundary();
jaroslav@640
   690
		neededBits = 32;
jaroslav@640
   691
		mode = DECODE_CHKSUM;
jaroslav@640
   692
		return true;
jaroslav@640
   693
	      }
jaroslav@640
   694
	  }
jaroslav@640
   695
jaroslav@640
   696
	int type = input.peekBits(3);
jaroslav@640
   697
	if (type < 0)
jaroslav@640
   698
	  return false;
jaroslav@640
   699
	input.dropBits(3);
jaroslav@640
   700
jaroslav@640
   701
	if ((type & 1) != 0)
jaroslav@640
   702
	  isLastBlock = true;
jaroslav@640
   703
	switch (type >> 1)
jaroslav@640
   704
	  {
jaroslav@640
   705
	  case DeflaterConstants.STORED_BLOCK:
jaroslav@640
   706
	    input.skipToByteBoundary();
jaroslav@640
   707
	    mode = DECODE_STORED_LEN1;
jaroslav@640
   708
	    break;
jaroslav@640
   709
	  case DeflaterConstants.STATIC_TREES:
jaroslav@640
   710
	    litlenTree = InflaterHuffmanTree.defLitLenTree;
jaroslav@640
   711
	    distTree = InflaterHuffmanTree.defDistTree;
jaroslav@640
   712
	    mode = DECODE_HUFFMAN;
jaroslav@640
   713
	    break;
jaroslav@640
   714
	  case DeflaterConstants.DYN_TREES:
jaroslav@640
   715
	    dynHeader = new InflaterDynHeader();
jaroslav@640
   716
	    mode = DECODE_DYN_HEADER;
jaroslav@640
   717
	    break;
jaroslav@640
   718
	  default:
jaroslav@640
   719
	    throw new DataFormatException("Unknown block type "+type);
jaroslav@640
   720
	  }
jaroslav@640
   721
	return true;
jaroslav@640
   722
jaroslav@640
   723
      case DECODE_STORED_LEN1:
jaroslav@640
   724
	{
jaroslav@640
   725
	  if ((uncomprLen = input.peekBits(16)) < 0)
jaroslav@640
   726
	    return false;
jaroslav@640
   727
	  input.dropBits(16);
jaroslav@640
   728
	  mode = DECODE_STORED_LEN2;
jaroslav@640
   729
	}
jaroslav@640
   730
	/* fall through */
jaroslav@640
   731
      case DECODE_STORED_LEN2:
jaroslav@640
   732
	{
jaroslav@640
   733
	  int nlen = input.peekBits(16);
jaroslav@640
   734
	  if (nlen < 0)
jaroslav@640
   735
	    return false;
jaroslav@640
   736
	  input.dropBits(16);
jaroslav@640
   737
	  if (nlen != (uncomprLen ^ 0xffff))
jaroslav@640
   738
	    throw new DataFormatException("broken uncompressed block");
jaroslav@640
   739
	  mode = DECODE_STORED;
jaroslav@640
   740
	}
jaroslav@640
   741
	/* fall through */
jaroslav@640
   742
      case DECODE_STORED:
jaroslav@640
   743
	{
jaroslav@640
   744
	  int more = outputWindow.copyStored(input, uncomprLen);
jaroslav@640
   745
	  uncomprLen -= more;
jaroslav@640
   746
	  if (uncomprLen == 0)
jaroslav@640
   747
	    {
jaroslav@640
   748
	      mode = DECODE_BLOCKS;
jaroslav@640
   749
	      return true;
jaroslav@640
   750
	    }
jaroslav@640
   751
	  return !input.needsInput();
jaroslav@640
   752
	}
jaroslav@640
   753
jaroslav@640
   754
      case DECODE_DYN_HEADER:
jaroslav@640
   755
	if (!dynHeader.decode(input))
jaroslav@640
   756
	  return false;
jaroslav@640
   757
	litlenTree = dynHeader.buildLitLenTree();
jaroslav@640
   758
	distTree = dynHeader.buildDistTree();
jaroslav@640
   759
	mode = DECODE_HUFFMAN;
jaroslav@640
   760
	/* fall through */
jaroslav@640
   761
      case DECODE_HUFFMAN:
jaroslav@640
   762
      case DECODE_HUFFMAN_LENBITS:
jaroslav@640
   763
      case DECODE_HUFFMAN_DIST:
jaroslav@640
   764
      case DECODE_HUFFMAN_DISTBITS:
jaroslav@640
   765
	return decodeHuffman();
jaroslav@640
   766
      case FINISHED:
jaroslav@640
   767
	return false;
jaroslav@640
   768
      default:
jaroslav@640
   769
	throw new IllegalStateException();
jaroslav@640
   770
      }	
jaroslav@640
   771
  }
jaroslav@640
   772
jaroslav@640
   773
jaroslav@640
   774
    interface DeflaterConstants {
jaroslav@640
   775
      final static boolean DEBUGGING = false;
jaroslav@640
   776
jaroslav@640
   777
      final static int STORED_BLOCK = 0;
jaroslav@640
   778
      final static int STATIC_TREES = 1;
jaroslav@640
   779
      final static int DYN_TREES    = 2;
jaroslav@640
   780
      final static int PRESET_DICT  = 0x20;
jaroslav@640
   781
jaroslav@640
   782
      final static int DEFAULT_MEM_LEVEL = 8;
jaroslav@640
   783
jaroslav@640
   784
      final static int MAX_MATCH = 258;
jaroslav@640
   785
      final static int MIN_MATCH = 3;
jaroslav@640
   786
jaroslav@640
   787
      final static int MAX_WBITS = 15;
jaroslav@640
   788
      final static int WSIZE = 1 << MAX_WBITS;
jaroslav@640
   789
      final static int WMASK = WSIZE - 1;
jaroslav@640
   790
jaroslav@640
   791
      final static int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
jaroslav@640
   792
      final static int HASH_SIZE = 1 << HASH_BITS;
jaroslav@640
   793
      final static int HASH_MASK = HASH_SIZE - 1;
jaroslav@640
   794
      final static int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
jaroslav@640
   795
jaroslav@640
   796
      final static int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
jaroslav@640
   797
      final static int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
jaroslav@640
   798
jaroslav@640
   799
      final static int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
jaroslav@640
   800
      final static int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5);
jaroslav@640
   801
jaroslav@640
   802
      final static int DEFLATE_STORED = 0;
jaroslav@640
   803
      final static int DEFLATE_FAST   = 1;
jaroslav@640
   804
      final static int DEFLATE_SLOW   = 2;
jaroslav@640
   805
jaroslav@640
   806
      final static int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8,  8,  8,  32,  32 };
jaroslav@640
   807
      final static int MAX_LAZY[]    = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 };
jaroslav@640
   808
      final static int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 };
jaroslav@640
   809
      final static int MAX_CHAIN[]   = { 0,4, 8,32,16,32,128,256,1024,4096 };
jaroslav@640
   810
      final static int COMPR_FUNC[]  = { 0,1, 1, 1, 1, 2,  2,  2,   2,   2 };
jaroslav@640
   811
    }
jaroslav@640
   812
    private static class InflaterHuffmanTree {
jaroslav@640
   813
      private final static int MAX_BITLEN = 15;
jaroslav@640
   814
      private short[] tree;
jaroslav@640
   815
jaroslav@640
   816
      public static InflaterHuffmanTree defLitLenTree, defDistTree;
jaroslav@640
   817
jaroslav@640
   818
      static
jaroslav@640
   819
      {
jaroslav@640
   820
        try 
jaroslav@640
   821
          {
jaroslav@640
   822
        byte[] codeLengths = new byte[288];
jaroslav@640
   823
        int i = 0;
jaroslav@640
   824
        while (i < 144)
jaroslav@640
   825
          codeLengths[i++] = 8;
jaroslav@640
   826
        while (i < 256)
jaroslav@640
   827
          codeLengths[i++] = 9;
jaroslav@640
   828
        while (i < 280)
jaroslav@640
   829
          codeLengths[i++] = 7;
jaroslav@640
   830
        while (i < 288)
jaroslav@640
   831
          codeLengths[i++] = 8;
jaroslav@640
   832
        defLitLenTree = new InflaterHuffmanTree(codeLengths);
jaroslav@640
   833
jaroslav@640
   834
        codeLengths = new byte[32];
jaroslav@640
   835
        i = 0;
jaroslav@640
   836
        while (i < 32)
jaroslav@640
   837
          codeLengths[i++] = 5;
jaroslav@640
   838
        defDistTree = new InflaterHuffmanTree(codeLengths);
jaroslav@640
   839
          } 
jaroslav@640
   840
        catch (DataFormatException ex)
jaroslav@640
   841
          {
jaroslav@640
   842
        throw new IllegalStateException
jaroslav@640
   843
          ("InflaterHuffmanTree: static tree length illegal");
jaroslav@640
   844
          }
jaroslav@640
   845
      }
jaroslav@640
   846
jaroslav@640
   847
      /**
jaroslav@640
   848
       * Constructs a Huffman tree from the array of code lengths.
jaroslav@640
   849
       *
jaroslav@640
   850
       * @param codeLengths the array of code lengths
jaroslav@640
   851
       */
jaroslav@640
   852
      public InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException
jaroslav@640
   853
      {
jaroslav@640
   854
        buildTree(codeLengths);
jaroslav@640
   855
      }
jaroslav@640
   856
jaroslav@640
   857
      private void buildTree(byte[] codeLengths) throws DataFormatException
jaroslav@640
   858
      {
jaroslav@640
   859
        int[] blCount = new int[MAX_BITLEN+1];
jaroslav@640
   860
        int[] nextCode = new int[MAX_BITLEN+1];
jaroslav@640
   861
        for (int i = 0; i < codeLengths.length; i++)
jaroslav@640
   862
          {
jaroslav@640
   863
        int bits = codeLengths[i];
jaroslav@640
   864
        if (bits > 0)
jaroslav@640
   865
          blCount[bits]++;
jaroslav@640
   866
          }
jaroslav@640
   867
jaroslav@640
   868
        int code = 0;
jaroslav@640
   869
        int treeSize = 512;
jaroslav@640
   870
        for (int bits = 1; bits <= MAX_BITLEN; bits++)
jaroslav@640
   871
          {
jaroslav@640
   872
        nextCode[bits] = code;
jaroslav@640
   873
        code += blCount[bits] << (16 - bits);
jaroslav@640
   874
        if (bits >= 10)
jaroslav@640
   875
          {
jaroslav@640
   876
            /* We need an extra table for bit lengths >= 10. */
jaroslav@640
   877
            int start = nextCode[bits] & 0x1ff80;
jaroslav@640
   878
            int end   = code & 0x1ff80;
jaroslav@640
   879
            treeSize += (end - start) >> (16 - bits);
jaroslav@640
   880
          }
jaroslav@640
   881
          }
jaroslav@640
   882
        if (code != 65536)
jaroslav@640
   883
          throw new DataFormatException("Code lengths don't add up properly.");
jaroslav@640
   884
jaroslav@687
   885
        fillTable1(treeSize, code, blCount);
jaroslav@640
   886
jaroslav@640
   887
        for (int i = 0; i < codeLengths.length; i++)
jaroslav@640
   888
          {
jaroslav@640
   889
        int bits = codeLengths[i];
jaroslav@640
   890
        if (bits == 0)
jaroslav@640
   891
          continue;
jaroslav@640
   892
        code = nextCode[bits];
jaroslav@640
   893
        int revcode = bitReverse(code);
jaroslav@640
   894
        if (bits <= 9)
jaroslav@640
   895
          {
jaroslav@640
   896
            do
jaroslav@640
   897
              {
jaroslav@640
   898
            tree[revcode] = (short) ((i << 4) | bits);
jaroslav@640
   899
            revcode += 1 << bits;
jaroslav@640
   900
              }
jaroslav@640
   901
            while (revcode < 512);
jaroslav@640
   902
          }
jaroslav@640
   903
        else
jaroslav@640
   904
          {
jaroslav@640
   905
            int subTree = tree[revcode & 511];
jaroslav@640
   906
            int treeLen = 1 << (subTree & 15);
jaroslav@640
   907
            subTree = -(subTree >> 4);
jaroslav@640
   908
            do
jaroslav@640
   909
              { 
jaroslav@640
   910
            tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);
jaroslav@640
   911
            revcode += 1 << bits;
jaroslav@640
   912
              }
jaroslav@640
   913
            while (revcode < treeLen);
jaroslav@640
   914
          }
jaroslav@640
   915
        nextCode[bits] = code + (1 << (16 - bits));
jaroslav@640
   916
          }
jaroslav@640
   917
      }
jaroslav@640
   918
      private final static String bit4Reverse =
jaroslav@640
   919
        "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017";
jaroslav@640
   920
      static short bitReverse(int value) {
jaroslav@640
   921
            return (short) (bit4Reverse.charAt(value & 0xf) << 12
jaroslav@687
   922
                    | bit4Reverse.charAt((value >> 4) & 0xf) << 8
jaroslav@687
   923
                    | bit4Reverse.charAt((value >> 8) & 0xf) << 4
jaroslav@687
   924
                    | bit4Reverse.charAt(value >> 12));
jaroslav@640
   925
      }
jaroslav@687
   926
      
jaroslav@640
   927
      /**
jaroslav@640
   928
       * Reads the next symbol from input.  The symbol is encoded using the
jaroslav@640
   929
       * huffman tree.
jaroslav@640
   930
       * @param input the input source.
jaroslav@640
   931
       * @return the next symbol, or -1 if not enough input is available.
jaroslav@640
   932
       */
jaroslav@640
   933
      public int getSymbol(StreamManipulator input) throws DataFormatException
jaroslav@640
   934
      {
jaroslav@640
   935
        int lookahead, symbol;
jaroslav@640
   936
        if ((lookahead = input.peekBits(9)) >= 0)
jaroslav@640
   937
          {
jaroslav@640
   938
        if ((symbol = tree[lookahead]) >= 0)
jaroslav@640
   939
          {
jaroslav@640
   940
            input.dropBits(symbol & 15);
jaroslav@640
   941
            return symbol >> 4;
jaroslav@640
   942
          }
jaroslav@640
   943
        int subtree = -(symbol >> 4);
jaroslav@640
   944
        int bitlen = symbol & 15;
jaroslav@640
   945
        if ((lookahead = input.peekBits(bitlen)) >= 0)
jaroslav@640
   946
          {
jaroslav@640
   947
            symbol = tree[subtree | (lookahead >> 9)];
jaroslav@640
   948
            input.dropBits(symbol & 15);
jaroslav@640
   949
            return symbol >> 4;
jaroslav@640
   950
          }
jaroslav@640
   951
        else
jaroslav@640
   952
          {
jaroslav@640
   953
            int bits = input.getAvailableBits();
jaroslav@640
   954
            lookahead = input.peekBits(bits);
jaroslav@640
   955
            symbol = tree[subtree | (lookahead >> 9)];
jaroslav@640
   956
            if ((symbol & 15) <= bits)
jaroslav@640
   957
              {
jaroslav@640
   958
            input.dropBits(symbol & 15);
jaroslav@640
   959
            return symbol >> 4;
jaroslav@640
   960
              }
jaroslav@640
   961
            else
jaroslav@640
   962
              return -1;
jaroslav@640
   963
          }
jaroslav@640
   964
          }
jaroslav@640
   965
        else
jaroslav@640
   966
          {
jaroslav@640
   967
        int bits = input.getAvailableBits();
jaroslav@640
   968
        lookahead = input.peekBits(bits);
jaroslav@640
   969
        symbol = tree[lookahead];
jaroslav@640
   970
        if (symbol >= 0 && (symbol & 15) <= bits)
jaroslav@640
   971
          {
jaroslav@640
   972
            input.dropBits(symbol & 15);
jaroslav@640
   973
            return symbol >> 4;
jaroslav@640
   974
          }
jaroslav@640
   975
        else
jaroslav@640
   976
          return -1;
jaroslav@640
   977
          }
jaroslav@640
   978
      }
jaroslav@687
   979
jaroslav@687
   980
        private void fillTable1(int treeSize, int code, int[] blCount) {
jaroslav@687
   981
            /* Now create and fill the extra tables from longest to shortest
jaroslav@687
   982
             * bit len.  This way the sub trees will be aligned.
jaroslav@687
   983
             */
jaroslav@687
   984
            tree = new short[treeSize];
jaroslav@687
   985
            int treePtr = 512;
jaroslav@687
   986
            for (int bits = MAX_BITLEN; bits >= 10; bits--) {
jaroslav@687
   987
                int end = code & 0x1ff80;
jaroslav@687
   988
                code -= blCount[bits] << (16 - bits);
jaroslav@687
   989
                int start = code & 0x1ff80;
jaroslav@687
   990
                final int inc = 1 << 7;
jaroslav@687
   991
                fillTable2(start, end, inc, treePtr, bits);
jaroslav@687
   992
            }
jaroslav@687
   993
        }
jaroslav@687
   994
jaroslav@687
   995
        private void fillTable2(int start, int end, final int inc, int treePtr, int bits) {
jaroslav@687
   996
            for (int i = start; i < end; i += inc) {
jaroslav@687
   997
                final short br = bitReverse(i);
jaroslav@687
   998
                tree[br] = (short) ((-treePtr << 4) | bits);
jaroslav@687
   999
                treePtr += 1 << (bits - 9);
jaroslav@687
  1000
            }
jaroslav@687
  1001
        }
jaroslav@640
  1002
    }
jaroslav@640
  1003
    private static class InflaterDynHeader
jaroslav@640
  1004
    {
jaroslav@640
  1005
      private static final int LNUM   = 0;
jaroslav@640
  1006
      private static final int DNUM   = 1;
jaroslav@640
  1007
      private static final int BLNUM  = 2;
jaroslav@640
  1008
      private static final int BLLENS = 3;
jaroslav@640
  1009
      private static final int LENS   = 4;
jaroslav@640
  1010
      private static final int REPS   = 5;
jaroslav@640
  1011
jaroslav@640
  1012
      private static final int repMin[]  = { 3, 3, 11 };
jaroslav@640
  1013
      private static final int repBits[] = { 2, 3,  7 };
jaroslav@640
  1014
jaroslav@640
  1015
jaroslav@640
  1016
      private byte[] blLens;
jaroslav@640
  1017
      private byte[] litdistLens;
jaroslav@640
  1018
jaroslav@640
  1019
      private InflaterHuffmanTree blTree;
jaroslav@640
  1020
jaroslav@640
  1021
      private int mode;
jaroslav@640
  1022
      private int lnum, dnum, blnum, num;
jaroslav@640
  1023
      private int repSymbol;
jaroslav@640
  1024
      private byte lastLen;
jaroslav@640
  1025
      private int ptr;
jaroslav@640
  1026
jaroslav@640
  1027
      private static final int[] BL_ORDER =
jaroslav@640
  1028
      { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
jaroslav@640
  1029
jaroslav@640
  1030
      public InflaterDynHeader()
jaroslav@640
  1031
      {
jaroslav@640
  1032
      }
jaroslav@640
  1033
jaroslav@640
  1034
      public boolean decode(StreamManipulator input) throws DataFormatException
jaroslav@640
  1035
      {
jaroslav@640
  1036
      decode_loop:
jaroslav@640
  1037
        for (;;)
jaroslav@640
  1038
          {
jaroslav@640
  1039
        switch (mode)
jaroslav@640
  1040
          {
jaroslav@640
  1041
          case LNUM:
jaroslav@640
  1042
            lnum = input.peekBits(5);
jaroslav@640
  1043
            if (lnum < 0)
jaroslav@640
  1044
              return false;
jaroslav@640
  1045
            lnum += 257;
jaroslav@640
  1046
            input.dropBits(5);
jaroslav@640
  1047
    //  	    System.err.println("LNUM: "+lnum);
jaroslav@640
  1048
            mode = DNUM;
jaroslav@640
  1049
            /* fall through */
jaroslav@640
  1050
          case DNUM:
jaroslav@640
  1051
            dnum = input.peekBits(5);
jaroslav@640
  1052
            if (dnum < 0)
jaroslav@640
  1053
              return false;
jaroslav@640
  1054
            dnum++;
jaroslav@640
  1055
            input.dropBits(5);
jaroslav@640
  1056
    //  	    System.err.println("DNUM: "+dnum);
jaroslav@640
  1057
            num = lnum+dnum;
jaroslav@640
  1058
            litdistLens = new byte[num];
jaroslav@640
  1059
            mode = BLNUM;
jaroslav@640
  1060
            /* fall through */
jaroslav@640
  1061
          case BLNUM:
jaroslav@640
  1062
            blnum = input.peekBits(4);
jaroslav@640
  1063
            if (blnum < 0)
jaroslav@640
  1064
              return false;
jaroslav@640
  1065
            blnum += 4;
jaroslav@640
  1066
            input.dropBits(4);
jaroslav@640
  1067
            blLens = new byte[19];
jaroslav@640
  1068
            ptr = 0;
jaroslav@640
  1069
    //  	    System.err.println("BLNUM: "+blnum);
jaroslav@640
  1070
            mode = BLLENS;
jaroslav@640
  1071
            /* fall through */
jaroslav@640
  1072
          case BLLENS:
jaroslav@640
  1073
            while (ptr < blnum)
jaroslav@640
  1074
              {
jaroslav@640
  1075
            int len = input.peekBits(3);
jaroslav@640
  1076
            if (len < 0)
jaroslav@640
  1077
              return false;
jaroslav@640
  1078
            input.dropBits(3);
jaroslav@640
  1079
    //  		System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);
jaroslav@640
  1080
            blLens[BL_ORDER[ptr]] = (byte) len;
jaroslav@640
  1081
            ptr++;
jaroslav@640
  1082
              }
jaroslav@640
  1083
            blTree = new InflaterHuffmanTree(blLens);
jaroslav@640
  1084
            blLens = null;
jaroslav@640
  1085
            ptr = 0;
jaroslav@640
  1086
            mode = LENS;
jaroslav@640
  1087
            /* fall through */
jaroslav@640
  1088
          case LENS:
jaroslav@640
  1089
            {
jaroslav@640
  1090
              int symbol;
jaroslav@640
  1091
              while (((symbol = blTree.getSymbol(input)) & ~15) == 0)
jaroslav@640
  1092
            {
jaroslav@640
  1093
              /* Normal case: symbol in [0..15] */
jaroslav@640
  1094
jaroslav@640
  1095
    //  		  System.err.println("litdistLens["+ptr+"]: "+symbol);
jaroslav@640
  1096
              litdistLens[ptr++] = lastLen = (byte) symbol;
jaroslav@640
  1097
jaroslav@640
  1098
              if (ptr == num)
jaroslav@640
  1099
                {
jaroslav@640
  1100
                  /* Finished */
jaroslav@640
  1101
                  return true;
jaroslav@640
  1102
                }
jaroslav@640
  1103
            }
jaroslav@640
  1104
jaroslav@640
  1105
              /* need more input ? */
jaroslav@640
  1106
              if (symbol < 0)
jaroslav@640
  1107
            return false;
jaroslav@640
  1108
jaroslav@640
  1109
              /* otherwise repeat code */
jaroslav@640
  1110
              if (symbol >= 17)
jaroslav@640
  1111
            {
jaroslav@640
  1112
              /* repeat zero */
jaroslav@640
  1113
    //  		  System.err.println("repeating zero");
jaroslav@640
  1114
              lastLen = 0;
jaroslav@640
  1115
            }
jaroslav@640
  1116
              else
jaroslav@640
  1117
            {
jaroslav@640
  1118
              if (ptr == 0)
jaroslav@640
  1119
                throw new DataFormatException();
jaroslav@640
  1120
            }
jaroslav@640
  1121
              repSymbol = symbol-16;
jaroslav@640
  1122
              mode = REPS;
jaroslav@640
  1123
            }
jaroslav@640
  1124
            /* fall through */
jaroslav@640
  1125
jaroslav@640
  1126
          case REPS:
jaroslav@640
  1127
            {
jaroslav@640
  1128
              int bits = repBits[repSymbol];
jaroslav@640
  1129
              int count = input.peekBits(bits);
jaroslav@640
  1130
              if (count < 0)
jaroslav@640
  1131
            return false;
jaroslav@640
  1132
              input.dropBits(bits);
jaroslav@640
  1133
              count += repMin[repSymbol];
jaroslav@640
  1134
    //  	      System.err.println("litdistLens repeated: "+count);
jaroslav@640
  1135
jaroslav@640
  1136
              if (ptr + count > num)
jaroslav@640
  1137
            throw new DataFormatException();
jaroslav@640
  1138
              while (count-- > 0)
jaroslav@640
  1139
            litdistLens[ptr++] = lastLen;
jaroslav@640
  1140
jaroslav@640
  1141
              if (ptr == num)
jaroslav@640
  1142
            {
jaroslav@640
  1143
              /* Finished */
jaroslav@640
  1144
              return true;
jaroslav@640
  1145
            }
jaroslav@640
  1146
            }
jaroslav@640
  1147
            mode = LENS;
jaroslav@640
  1148
            continue decode_loop;
jaroslav@640
  1149
          }
jaroslav@640
  1150
          }
jaroslav@640
  1151
      }
jaroslav@640
  1152
jaroslav@640
  1153
      public InflaterHuffmanTree buildLitLenTree() throws DataFormatException
jaroslav@640
  1154
      {
jaroslav@640
  1155
        byte[] litlenLens = new byte[lnum];
jaroslav@640
  1156
        System.arraycopy(litdistLens, 0, litlenLens, 0, lnum);
jaroslav@640
  1157
        return new InflaterHuffmanTree(litlenLens);
jaroslav@640
  1158
      }
jaroslav@640
  1159
jaroslav@640
  1160
      public InflaterHuffmanTree buildDistTree() throws DataFormatException
jaroslav@640
  1161
      {
jaroslav@640
  1162
        byte[] distLens = new byte[dnum];
jaroslav@640
  1163
        System.arraycopy(litdistLens, lnum, distLens, 0, dnum);
jaroslav@640
  1164
        return new InflaterHuffmanTree(distLens);
jaroslav@640
  1165
      }
jaroslav@640
  1166
    }
jaroslav@609
  1167
    /**
jaroslav@640
  1168
     * This class allows us to retrieve a specified amount of bits from
jaroslav@640
  1169
     * the input buffer, as well as copy big byte blocks.
jaroslav@609
  1170
     *
jaroslav@640
  1171
     * It uses an int buffer to store up to 31 bits for direct
jaroslav@640
  1172
     * manipulation.  This guarantees that we can get at least 16 bits,
jaroslav@640
  1173
     * but we only need at most 15, so this is all safe.
jaroslav@640
  1174
     *
jaroslav@640
  1175
     * There are some optimizations in this class, for example, you must
jaroslav@640
  1176
     * never peek more then 8 bits more than needed, and you must first 
jaroslav@640
  1177
     * peek bits before you may drop them.  This is not a general purpose
jaroslav@640
  1178
     * class but optimized for the behaviour of the Inflater.
jaroslav@640
  1179
     *
jaroslav@640
  1180
     * @author John Leuner, Jochen Hoenicke
jaroslav@609
  1181
     */
jaroslav@640
  1182
jaroslav@640
  1183
    private static class StreamManipulator
jaroslav@640
  1184
    {
jaroslav@640
  1185
      private byte[] window;
jaroslav@640
  1186
      private int window_start = 0;
jaroslav@640
  1187
      private int window_end = 0;
jaroslav@640
  1188
jaroslav@640
  1189
      private int buffer = 0;
jaroslav@640
  1190
      private int bits_in_buffer = 0;
jaroslav@640
  1191
jaroslav@640
  1192
      /**
jaroslav@640
  1193
       * Get the next n bits but don't increase input pointer.  n must be
jaroslav@640
  1194
       * less or equal 16 and if you if this call succeeds, you must drop
jaroslav@640
  1195
       * at least n-8 bits in the next call.
jaroslav@640
  1196
       * 
jaroslav@640
  1197
       * @return the value of the bits, or -1 if not enough bits available.  */
jaroslav@640
  1198
      public final int peekBits(int n)
jaroslav@640
  1199
      {
jaroslav@640
  1200
        if (bits_in_buffer < n)
jaroslav@640
  1201
          {
jaroslav@640
  1202
        if (window_start == window_end)
jaroslav@640
  1203
          return -1;
jaroslav@640
  1204
        buffer |= (window[window_start++] & 0xff
jaroslav@640
  1205
               | (window[window_start++] & 0xff) << 8) << bits_in_buffer;
jaroslav@640
  1206
        bits_in_buffer += 16;
jaroslav@640
  1207
          }
jaroslav@640
  1208
        return buffer & ((1 << n) - 1);
jaroslav@640
  1209
      }
jaroslav@640
  1210
jaroslav@640
  1211
      /* Drops the next n bits from the input.  You should have called peekBits
jaroslav@640
  1212
       * with a bigger or equal n before, to make sure that enough bits are in
jaroslav@640
  1213
       * the bit buffer.
jaroslav@640
  1214
       */
jaroslav@640
  1215
      public final void dropBits(int n)
jaroslav@640
  1216
      {
jaroslav@640
  1217
        buffer >>>= n;
jaroslav@640
  1218
        bits_in_buffer -= n;
jaroslav@640
  1219
      }
jaroslav@640
  1220
jaroslav@640
  1221
      /**
jaroslav@640
  1222
       * Gets the next n bits and increases input pointer.  This is equivalent
jaroslav@640
  1223
       * to peekBits followed by dropBits, except for correct error handling.
jaroslav@640
  1224
       * @return the value of the bits, or -1 if not enough bits available. 
jaroslav@640
  1225
       */
jaroslav@640
  1226
      public final int getBits(int n)
jaroslav@640
  1227
      {
jaroslav@640
  1228
        int bits = peekBits(n);
jaroslav@640
  1229
        if (bits >= 0)
jaroslav@640
  1230
          dropBits(n);
jaroslav@640
  1231
        return bits;
jaroslav@640
  1232
      }
jaroslav@640
  1233
      /**
jaroslav@640
  1234
       * Gets the number of bits available in the bit buffer.  This must be
jaroslav@640
  1235
       * only called when a previous peekBits() returned -1.
jaroslav@640
  1236
       * @return the number of bits available.
jaroslav@640
  1237
       */
jaroslav@640
  1238
      public final int getAvailableBits()
jaroslav@640
  1239
      {
jaroslav@640
  1240
        return bits_in_buffer;
jaroslav@640
  1241
      }
jaroslav@640
  1242
jaroslav@640
  1243
      /**
jaroslav@640
  1244
       * Gets the number of bytes available.  
jaroslav@640
  1245
       * @return the number of bytes available.
jaroslav@640
  1246
       */
jaroslav@640
  1247
      public final int getAvailableBytes()
jaroslav@640
  1248
      {
jaroslav@640
  1249
        return window_end - window_start + (bits_in_buffer >> 3);
jaroslav@640
  1250
      }
jaroslav@640
  1251
jaroslav@640
  1252
      /**
jaroslav@640
  1253
       * Skips to the next byte boundary.
jaroslav@640
  1254
       */
jaroslav@640
  1255
      public void skipToByteBoundary()
jaroslav@640
  1256
      {
jaroslav@640
  1257
        buffer >>= (bits_in_buffer & 7);
jaroslav@640
  1258
        bits_in_buffer &= ~7;
jaroslav@640
  1259
      }
jaroslav@640
  1260
jaroslav@640
  1261
      public final boolean needsInput() {
jaroslav@640
  1262
        return window_start == window_end;
jaroslav@640
  1263
      }
jaroslav@640
  1264
jaroslav@640
  1265
jaroslav@640
  1266
      /* Copies length bytes from input buffer to output buffer starting
jaroslav@640
  1267
       * at output[offset].  You have to make sure, that the buffer is
jaroslav@640
  1268
       * byte aligned.  If not enough bytes are available, copies fewer
jaroslav@640
  1269
       * bytes.
jaroslav@640
  1270
       * @param length the length to copy, 0 is allowed.
jaroslav@640
  1271
       * @return the number of bytes copied, 0 if no byte is available.  
jaroslav@640
  1272
       */
jaroslav@640
  1273
      public int copyBytes(byte[] output, int offset, int length)
jaroslav@640
  1274
      {
jaroslav@640
  1275
        if (length < 0)
jaroslav@640
  1276
          throw new IllegalArgumentException("length negative");
jaroslav@640
  1277
        if ((bits_in_buffer & 7) != 0)  
jaroslav@640
  1278
          /* bits_in_buffer may only be 0 or 8 */
jaroslav@640
  1279
          throw new IllegalStateException("Bit buffer is not aligned!");
jaroslav@640
  1280
jaroslav@640
  1281
        int count = 0;
jaroslav@640
  1282
        while (bits_in_buffer > 0 && length > 0)
jaroslav@640
  1283
          {
jaroslav@640
  1284
        output[offset++] = (byte) buffer;
jaroslav@640
  1285
        buffer >>>= 8;
jaroslav@640
  1286
        bits_in_buffer -= 8;
jaroslav@640
  1287
        length--;
jaroslav@640
  1288
        count++;
jaroslav@640
  1289
          }
jaroslav@640
  1290
        if (length == 0)
jaroslav@640
  1291
          return count;
jaroslav@640
  1292
jaroslav@640
  1293
        int avail = window_end - window_start;
jaroslav@640
  1294
        if (length > avail)
jaroslav@640
  1295
          length = avail;
jaroslav@640
  1296
        System.arraycopy(window, window_start, output, offset, length);
jaroslav@640
  1297
        window_start += length;
jaroslav@640
  1298
jaroslav@640
  1299
        if (((window_start - window_end) & 1) != 0)
jaroslav@640
  1300
          {
jaroslav@640
  1301
        /* We always want an even number of bytes in input, see peekBits */
jaroslav@640
  1302
        buffer = (window[window_start++] & 0xff);
jaroslav@640
  1303
        bits_in_buffer = 8;
jaroslav@640
  1304
          }
jaroslav@640
  1305
        return count + length;
jaroslav@640
  1306
      }
jaroslav@640
  1307
jaroslav@640
  1308
      public StreamManipulator()
jaroslav@640
  1309
      {
jaroslav@640
  1310
      }
jaroslav@640
  1311
jaroslav@640
  1312
      public void reset()
jaroslav@640
  1313
      {
jaroslav@640
  1314
        window_start = window_end = buffer = bits_in_buffer = 0;
jaroslav@640
  1315
      }
jaroslav@640
  1316
jaroslav@640
  1317
      public void setInput(byte[] buf, int off, int len)
jaroslav@640
  1318
      {
jaroslav@640
  1319
        if (window_start < window_end)
jaroslav@640
  1320
          throw new IllegalStateException
jaroslav@640
  1321
        ("Old input was not completely processed");
jaroslav@640
  1322
jaroslav@640
  1323
        int end = off + len;
jaroslav@640
  1324
jaroslav@640
  1325
        /* We want to throw an ArrayIndexOutOfBoundsException early.  The
jaroslav@640
  1326
         * check is very tricky: it also handles integer wrap around.  
jaroslav@640
  1327
         */
jaroslav@640
  1328
        if (0 > off || off > end || end > buf.length)
jaroslav@640
  1329
          throw new ArrayIndexOutOfBoundsException();
jaroslav@640
  1330
jaroslav@640
  1331
        if ((len & 1) != 0)
jaroslav@640
  1332
          {
jaroslav@640
  1333
        /* We always want an even number of bytes in input, see peekBits */
jaroslav@640
  1334
        buffer |= (buf[off++] & 0xff) << bits_in_buffer;
jaroslav@640
  1335
        bits_in_buffer += 8;
jaroslav@640
  1336
          }
jaroslav@640
  1337
jaroslav@640
  1338
        window = buf;
jaroslav@640
  1339
        window_start = off;
jaroslav@640
  1340
        window_end = end;
jaroslav@640
  1341
      }
jaroslav@609
  1342
    }
jaroslav@640
  1343
    /*
jaroslav@640
  1344
     * Contains the output from the Inflation process.
jaroslav@640
  1345
     *
jaroslav@640
  1346
     * We need to have a window so that we can refer backwards into the output stream
jaroslav@640
  1347
     * to repeat stuff.
jaroslav@640
  1348
     *
jaroslav@640
  1349
     * @author John Leuner
jaroslav@640
  1350
     * @since JDK 1.1
jaroslav@640
  1351
     */
jaroslav@609
  1352
jaroslav@640
  1353
    private static class OutputWindow
jaroslav@640
  1354
    {
jaroslav@640
  1355
      private final int WINDOW_SIZE = 1 << 15;
jaroslav@640
  1356
      private final int WINDOW_MASK = WINDOW_SIZE - 1;
jaroslav@640
  1357
jaroslav@640
  1358
      private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes
jaroslav@640
  1359
      private int window_end  = 0;
jaroslav@640
  1360
      private int window_filled = 0;
jaroslav@640
  1361
jaroslav@640
  1362
      public void write(int abyte)
jaroslav@640
  1363
      {
jaroslav@640
  1364
        if (window_filled++ == WINDOW_SIZE)
jaroslav@640
  1365
          throw new IllegalStateException("Window full");
jaroslav@640
  1366
        window[window_end++] = (byte) abyte;
jaroslav@640
  1367
        window_end &= WINDOW_MASK;
jaroslav@640
  1368
      }
jaroslav@640
  1369
jaroslav@640
  1370
jaroslav@640
  1371
      private final void slowRepeat(int rep_start, int len, int dist)
jaroslav@640
  1372
      {
jaroslav@640
  1373
        while (len-- > 0)
jaroslav@640
  1374
          {
jaroslav@640
  1375
        window[window_end++] = window[rep_start++];
jaroslav@640
  1376
        window_end &= WINDOW_MASK;
jaroslav@640
  1377
        rep_start &= WINDOW_MASK;
jaroslav@640
  1378
          }
jaroslav@640
  1379
      }
jaroslav@640
  1380
jaroslav@640
  1381
      public void repeat(int len, int dist)
jaroslav@640
  1382
      {
jaroslav@640
  1383
        if ((window_filled += len) > WINDOW_SIZE)
jaroslav@640
  1384
          throw new IllegalStateException("Window full");
jaroslav@640
  1385
jaroslav@640
  1386
        int rep_start = (window_end - dist) & WINDOW_MASK;
jaroslav@640
  1387
        int border = WINDOW_SIZE - len;
jaroslav@640
  1388
        if (rep_start <= border && window_end < border)
jaroslav@640
  1389
          {
jaroslav@640
  1390
        if (len <= dist)
jaroslav@640
  1391
          {
jaroslav@640
  1392
            System.arraycopy(window, rep_start, window, window_end, len);
jaroslav@640
  1393
            window_end += len;
jaroslav@640
  1394
          }
jaroslav@640
  1395
        else
jaroslav@640
  1396
          {
jaroslav@640
  1397
            /* We have to copy manually, since the repeat pattern overlaps.
jaroslav@640
  1398
             */
jaroslav@640
  1399
            while (len-- > 0)
jaroslav@640
  1400
              window[window_end++] = window[rep_start++];
jaroslav@640
  1401
          }
jaroslav@640
  1402
          }
jaroslav@640
  1403
        else
jaroslav@640
  1404
          slowRepeat(rep_start, len, dist);
jaroslav@640
  1405
      }
jaroslav@640
  1406
jaroslav@640
  1407
      public int copyStored(StreamManipulator input, int len)
jaroslav@640
  1408
      {
jaroslav@640
  1409
        len = Math.min(Math.min(len, WINDOW_SIZE - window_filled), 
jaroslav@640
  1410
               input.getAvailableBytes());
jaroslav@640
  1411
        int copied;
jaroslav@640
  1412
jaroslav@640
  1413
        int tailLen = WINDOW_SIZE - window_end;
jaroslav@640
  1414
        if (len > tailLen)
jaroslav@640
  1415
          {
jaroslav@640
  1416
        copied = input.copyBytes(window, window_end, tailLen);
jaroslav@640
  1417
        if (copied == tailLen)
jaroslav@640
  1418
          copied += input.copyBytes(window, 0, len - tailLen);
jaroslav@640
  1419
          }
jaroslav@640
  1420
        else
jaroslav@640
  1421
          copied = input.copyBytes(window, window_end, len);
jaroslav@640
  1422
jaroslav@640
  1423
        window_end = (window_end + copied) & WINDOW_MASK;
jaroslav@640
  1424
        window_filled += copied;
jaroslav@640
  1425
        return copied;
jaroslav@640
  1426
      }
jaroslav@640
  1427
jaroslav@640
  1428
      public void copyDict(byte[] dict, int offset, int len)
jaroslav@640
  1429
      {
jaroslav@640
  1430
        if (window_filled > 0)
jaroslav@640
  1431
          throw new IllegalStateException();
jaroslav@640
  1432
jaroslav@640
  1433
        if (len > WINDOW_SIZE)
jaroslav@640
  1434
          {
jaroslav@640
  1435
        offset += len - WINDOW_SIZE;
jaroslav@640
  1436
        len = WINDOW_SIZE;
jaroslav@640
  1437
          }
jaroslav@640
  1438
        System.arraycopy(dict, offset, window, 0, len);
jaroslav@640
  1439
        window_end = len & WINDOW_MASK;
jaroslav@640
  1440
      }
jaroslav@640
  1441
jaroslav@640
  1442
      public int getFreeSpace()
jaroslav@640
  1443
      {
jaroslav@640
  1444
        return WINDOW_SIZE - window_filled;
jaroslav@640
  1445
      }
jaroslav@640
  1446
jaroslav@640
  1447
      public int getAvailable()
jaroslav@640
  1448
      {
jaroslav@640
  1449
        return window_filled;
jaroslav@640
  1450
      }
jaroslav@640
  1451
jaroslav@640
  1452
      public int copyOutput(byte[] output, int offset, int len)
jaroslav@640
  1453
      {
jaroslav@640
  1454
        int copy_end = window_end;
jaroslav@640
  1455
        if (len > window_filled)
jaroslav@640
  1456
          len = window_filled;
jaroslav@640
  1457
        else
jaroslav@640
  1458
          copy_end = (window_end - window_filled + len) & WINDOW_MASK;
jaroslav@640
  1459
jaroslav@640
  1460
        int copied = len;
jaroslav@640
  1461
        int tailLen = len - copy_end;
jaroslav@640
  1462
jaroslav@640
  1463
        if (tailLen > 0)
jaroslav@640
  1464
          {
jaroslav@640
  1465
        System.arraycopy(window, WINDOW_SIZE - tailLen,
jaroslav@640
  1466
                 output, offset, tailLen);
jaroslav@640
  1467
        offset += tailLen;
jaroslav@640
  1468
        len = copy_end;
jaroslav@640
  1469
          }
jaroslav@640
  1470
        System.arraycopy(window, copy_end - len, output, offset, len);
jaroslav@640
  1471
        window_filled -= copied;
jaroslav@640
  1472
        if (window_filled < 0)
jaroslav@640
  1473
          throw new IllegalStateException();
jaroslav@640
  1474
        return copied;
jaroslav@640
  1475
      }
jaroslav@640
  1476
jaroslav@640
  1477
      public void reset() {
jaroslav@640
  1478
        window_filled = window_end = 0;
jaroslav@640
  1479
      }
jaroslav@609
  1480
    }
jaroslav@640
  1481
  
jaroslav@609
  1482
}