# HG changeset patch
# User Jaroslav Tulach
+ * The specification for Adler32 may be found in RFC 1950.
+ * (ZLIB Compressed Data Format Specification version 3.3)
+ *
+ *
+ * From that document:
+ *
+ * "ADLER32 (Adler-32 checksum)
+ * This contains a checksum value of the uncompressed data
+ * (excluding any dictionary data) computed according to Adler-32
+ * algorithm. This algorithm is a 32-bit extension and improvement
+ * of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
+ * standard.
+ *
+ * Adler-32 is composed of two sums accumulated per byte: s1 is
+ * the sum of all bytes, s2 is the sum of all s1 values. Both sums
+ * are done modulo 65521. s1 is initialized to 1, s2 to zero. The
+ * Adler-32 checksum is stored as s2*65536 + s1 in most-
+ * significant-byte first (network) order."
+ *
+ * "8.2. The Adler-32 algorithm
+ *
+ * The Adler-32 algorithm is much faster than the CRC32 algorithm yet
+ * still provides an extremely low probability of undetected errors.
+ *
+ * The modulo on unsigned long accumulators can be delayed for 5552
+ * bytes, so the modulo operation time is negligible. If the bytes
+ * are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
+ * and order sensitive, unlike the first sum, which is just a
+ * checksum. That 65521 is prime is important to avoid a possible
+ * large class of two-byte errors that leave the check unchanged.
+ * (The Fletcher checksum uses 255, which is not prime and which also
+ * makes the Fletcher check insensitive to single byte changes 0 <->
+ * 255.)
+ *
+ * The sum s1 is initialized to 1 instead of zero to make the length
+ * of the sequence part of s2, so that the length does not have to be
+ * checked separately. (Any sequence of zeroes has a Fletcher
+ * checksum of zero.)"
+ *
+ * @author John Leuner, Per Bothner
+ * @since JDK 1.1
+ *
+ * @see InflaterInputStream
+ * @see DeflaterOutputStream
+ */
+public class Adler32 implements Checksum
+{
+
+ /** largest prime smaller than 65536 */
+ private static final int BASE = 65521;
+
+ private int checksum; //we do all in int.
+
+ //Note that java doesn't have unsigned integers,
+ //so we have to be careful with what arithmetic
+ //we do. We return the checksum as a long to
+ //avoid sign confusion.
+
+ /**
+ * Creates a new instance of the
- * Note: When using the 'nowrap' option it is also necessary to provide
- * an extra "dummy" byte as input. This is required by the ZLIB native
- * library in order to support certain optimizations.
+ * This class allows us to retrieve a specified amount of bits from
+ * the input buffer, as well as copy big byte blocks.
*
- * @param nowrap if true then support GZIP compatible compression
+ * It uses an int buffer to store up to 31 bits for direct
+ * manipulation. This guarantees that we can get at least 16 bits,
+ * but we only need at most 15, so this is all safe.
+ *
+ * There are some optimizations in this class, for example, you must
+ * never peek more then 8 bits more than needed, and you must first
+ * peek bits before you may drop them. This is not a general purpose
+ * class but optimized for the behaviour of the Inflater.
+ *
+ * @author John Leuner, Jochen Hoenicke
*/
- public Inflater(boolean nowrap) {
+
+ private static class StreamManipulator
+ {
+ private byte[] window;
+ private int window_start = 0;
+ private int window_end = 0;
+
+ private int buffer = 0;
+ private int bits_in_buffer = 0;
+
+ /**
+ * Get the next n bits but don't increase input pointer. n must be
+ * less or equal 16 and if you if this call succeeds, you must drop
+ * at least n-8 bits in the next call.
+ *
+ * @return the value of the bits, or -1 if not enough bits available. */
+ public final int peekBits(int n)
+ {
+ if (bits_in_buffer < n)
+ {
+ if (window_start == window_end)
+ return -1;
+ buffer |= (window[window_start++] & 0xff
+ | (window[window_start++] & 0xff) << 8) << bits_in_buffer;
+ bits_in_buffer += 16;
+ }
+ return buffer & ((1 << n) - 1);
+ }
+
+ /* Drops the next n bits from the input. You should have called peekBits
+ * with a bigger or equal n before, to make sure that enough bits are in
+ * the bit buffer.
+ */
+ public final void dropBits(int n)
+ {
+ buffer >>>= n;
+ bits_in_buffer -= n;
+ }
+
+ /**
+ * Gets the next n bits and increases input pointer. This is equivalent
+ * to peekBits followed by dropBits, except for correct error handling.
+ * @return the value of the bits, or -1 if not enough bits available.
+ */
+ public final int getBits(int n)
+ {
+ int bits = peekBits(n);
+ if (bits >= 0)
+ dropBits(n);
+ return bits;
+ }
+ /**
+ * Gets the number of bits available in the bit buffer. This must be
+ * only called when a previous peekBits() returned -1.
+ * @return the number of bits available.
+ */
+ public final int getAvailableBits()
+ {
+ return bits_in_buffer;
+ }
+
+ /**
+ * Gets the number of bytes available.
+ * @return the number of bytes available.
+ */
+ public final int getAvailableBytes()
+ {
+ return window_end - window_start + (bits_in_buffer >> 3);
+ }
+
+ /**
+ * Skips to the next byte boundary.
+ */
+ public void skipToByteBoundary()
+ {
+ buffer >>= (bits_in_buffer & 7);
+ bits_in_buffer &= ~7;
+ }
+
+ public final boolean needsInput() {
+ return window_start == window_end;
+ }
+
+
+ /* Copies length bytes from input buffer to output buffer starting
+ * at output[offset]. You have to make sure, that the buffer is
+ * byte aligned. If not enough bytes are available, copies fewer
+ * bytes.
+ * @param length the length to copy, 0 is allowed.
+ * @return the number of bytes copied, 0 if no byte is available.
+ */
+ public int copyBytes(byte[] output, int offset, int length)
+ {
+ if (length < 0)
+ throw new IllegalArgumentException("length negative");
+ if ((bits_in_buffer & 7) != 0)
+ /* bits_in_buffer may only be 0 or 8 */
+ throw new IllegalStateException("Bit buffer is not aligned!");
+
+ int count = 0;
+ while (bits_in_buffer > 0 && length > 0)
+ {
+ output[offset++] = (byte) buffer;
+ buffer >>>= 8;
+ bits_in_buffer -= 8;
+ length--;
+ count++;
+ }
+ if (length == 0)
+ return count;
+
+ int avail = window_end - window_start;
+ if (length > avail)
+ length = avail;
+ System.arraycopy(window, window_start, output, offset, length);
+ window_start += length;
+
+ if (((window_start - window_end) & 1) != 0)
+ {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer = (window[window_start++] & 0xff);
+ bits_in_buffer = 8;
+ }
+ return count + length;
+ }
+
+ public StreamManipulator()
+ {
+ }
+
+ public void reset()
+ {
+ window_start = window_end = buffer = bits_in_buffer = 0;
+ }
+
+ public void setInput(byte[] buf, int off, int len)
+ {
+ if (window_start < window_end)
+ throw new IllegalStateException
+ ("Old input was not completely processed");
+
+ int end = off + len;
+
+ /* We want to throw an ArrayIndexOutOfBoundsException early. The
+ * check is very tricky: it also handles integer wrap around.
+ */
+ if (0 > off || off > end || end > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+
+ if ((len & 1) != 0)
+ {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer |= (buf[off++] & 0xff) << bits_in_buffer;
+ bits_in_buffer += 8;
+ }
+
+ window = buf;
+ window_start = off;
+ window_end = end;
+ }
}
+ /*
+ * Contains the output from the Inflation process.
+ *
+ * We need to have a window so that we can refer backwards into the output stream
+ * to repeat stuff.
+ *
+ * @author John Leuner
+ * @since JDK 1.1
+ */
- /**
- * Creates a new decompressor.
- */
- public Inflater() {
- this(false);
+ private static class OutputWindow
+ {
+ private final int WINDOW_SIZE = 1 << 15;
+ private final int WINDOW_MASK = WINDOW_SIZE - 1;
+
+ private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes
+ private int window_end = 0;
+ private int window_filled = 0;
+
+ public void write(int abyte)
+ {
+ if (window_filled++ == WINDOW_SIZE)
+ throw new IllegalStateException("Window full");
+ window[window_end++] = (byte) abyte;
+ window_end &= WINDOW_MASK;
+ }
+
+
+ private final void slowRepeat(int rep_start, int len, int dist)
+ {
+ while (len-- > 0)
+ {
+ window[window_end++] = window[rep_start++];
+ window_end &= WINDOW_MASK;
+ rep_start &= WINDOW_MASK;
+ }
+ }
+
+ public void repeat(int len, int dist)
+ {
+ if ((window_filled += len) > WINDOW_SIZE)
+ throw new IllegalStateException("Window full");
+
+ int rep_start = (window_end - dist) & WINDOW_MASK;
+ int border = WINDOW_SIZE - len;
+ if (rep_start <= border && window_end < border)
+ {
+ if (len <= dist)
+ {
+ System.arraycopy(window, rep_start, window, window_end, len);
+ window_end += len;
+ }
+ else
+ {
+ /* We have to copy manually, since the repeat pattern overlaps.
+ */
+ while (len-- > 0)
+ window[window_end++] = window[rep_start++];
+ }
+ }
+ else
+ slowRepeat(rep_start, len, dist);
+ }
+
+ public int copyStored(StreamManipulator input, int len)
+ {
+ len = Math.min(Math.min(len, WINDOW_SIZE - window_filled),
+ input.getAvailableBytes());
+ int copied;
+
+ int tailLen = WINDOW_SIZE - window_end;
+ if (len > tailLen)
+ {
+ copied = input.copyBytes(window, window_end, tailLen);
+ if (copied == tailLen)
+ copied += input.copyBytes(window, 0, len - tailLen);
+ }
+ else
+ copied = input.copyBytes(window, window_end, len);
+
+ window_end = (window_end + copied) & WINDOW_MASK;
+ window_filled += copied;
+ return copied;
+ }
+
+ public void copyDict(byte[] dict, int offset, int len)
+ {
+ if (window_filled > 0)
+ throw new IllegalStateException();
+
+ if (len > WINDOW_SIZE)
+ {
+ offset += len - WINDOW_SIZE;
+ len = WINDOW_SIZE;
+ }
+ System.arraycopy(dict, offset, window, 0, len);
+ window_end = len & WINDOW_MASK;
+ }
+
+ public int getFreeSpace()
+ {
+ return WINDOW_SIZE - window_filled;
+ }
+
+ public int getAvailable()
+ {
+ return window_filled;
+ }
+
+ public int copyOutput(byte[] output, int offset, int len)
+ {
+ int copy_end = window_end;
+ if (len > window_filled)
+ len = window_filled;
+ else
+ copy_end = (window_end - window_filled + len) & WINDOW_MASK;
+
+ int copied = len;
+ int tailLen = len - copy_end;
+
+ if (tailLen > 0)
+ {
+ System.arraycopy(window, WINDOW_SIZE - tailLen,
+ output, offset, tailLen);
+ offset += tailLen;
+ len = copy_end;
+ }
+ System.arraycopy(window, copy_end - len, output, offset, len);
+ window_filled -= copied;
+ if (window_filled < 0)
+ throw new IllegalStateException();
+ return copied;
+ }
+
+ public void reset() {
+ window_filled = window_end = 0;
+ }
}
-
- /**
- * Sets input data for decompression. Should be called whenever
- * needsInput() returns true indicating that more input data is
- * required.
- * @param b the input data bytes
- * @param off the start offset of the input data
- * @param len the length of the input data
- * @see Inflater#needsInput
- */
- public void setInput(byte[] b, int off, int len) {
- if (b == null) {
- throw new NullPointerException();
- }
- if (off < 0 || len < 0 || off > b.length - len) {
- throw new ArrayIndexOutOfBoundsException();
- }
- data = (String) infl(b, off, len);
- }
-
- /**
- * Sets input data for decompression. Should be called whenever
- * needsInput() returns true indicating that more input data is
- * required.
- * @param b the input data bytes
- * @see Inflater#needsInput
- */
- public void setInput(byte[] b) {
- setInput(b, 0, b.length);
- }
-
- /**
- * Sets the preset dictionary to the given array of bytes. Should be
- * called when inflate() returns 0 and needsDictionary() returns true
- * indicating that a preset dictionary is required. The method getAdler()
- * can be used to get the Adler-32 value of the dictionary needed.
- * @param b the dictionary data bytes
- * @param off the start offset of the data
- * @param len the length of the data
- * @see Inflater#needsDictionary
- * @see Inflater#getAdler
- */
- public void setDictionary(byte[] b, int off, int len) {
- if (b == null) {
- throw new NullPointerException();
- }
- if (off < 0 || len < 0 || off > b.length - len) {
- throw new ArrayIndexOutOfBoundsException();
- }
- needDict = false;
- }
-
- /**
- * Sets the preset dictionary to the given array of bytes. Should be
- * called when inflate() returns 0 and needsDictionary() returns true
- * indicating that a preset dictionary is required. The method getAdler()
- * can be used to get the Adler-32 value of the dictionary needed.
- * @param b the dictionary data bytes
- * @see Inflater#needsDictionary
- * @see Inflater#getAdler
- */
- public void setDictionary(byte[] b) {
- setDictionary(b, 0, b.length);
- }
-
- /**
- * Returns the total number of bytes remaining in the input buffer.
- * This can be used to find out what bytes still remain in the input
- * buffer after decompression has finished.
- * @return the total number of bytes remaining in the input buffer
- */
- public int getRemaining() {
- return data.length() - offset;
- }
-
- /**
- * Returns true if no data remains in the input buffer. This can
- * be used to determine if #setInput should be called in order
- * to provide more input.
- * @return true if no data remains in the input buffer
- */
- public boolean needsInput() {
- return getRemaining() <= 0;
- }
-
- /**
- * Returns true if a preset dictionary is needed for decompression.
- * @return true if a preset dictionary is needed for decompression
- * @see Inflater#setDictionary
- */
- public boolean needsDictionary() {
- return needDict;
- }
-
- /**
- * Returns true if the end of the compressed data stream has been
- * reached.
- * @return true if the end of the compressed data stream has been
- * reached
- */
- public boolean finished() {
- return finished;
- }
-
- /**
- * Uncompresses bytes into specified buffer. Returns actual number
- * of bytes uncompressed. A return value of 0 indicates that
- * needsInput() or needsDictionary() should be called in order to
- * determine if more input data or a preset dictionary is required.
- * In the latter case, getAdler() can be used to get the Adler-32
- * value of the dictionary required.
- * @param b the buffer for the uncompressed data
- * @param off the start offset of the data
- * @param len the maximum number of uncompressed bytes
- * @return the actual number of uncompressed bytes
- * @exception DataFormatException if the compressed data format is invalid
- * @see Inflater#needsInput
- * @see Inflater#needsDictionary
- */
- public int inflate(byte[] b, int off, int len)
- throws DataFormatException
- {
- if (b == null) {
- throw new NullPointerException();
- }
- if (off < 0 || len < 0 || off > b.length - len) {
- throw new ArrayIndexOutOfBoundsException();
- }
- int cnt = 0;
- while (offset < data.length()) {
- b[off++] = (byte)data.charAt(offset++);
- cnt++;
- counter++;
- }
- return cnt;
- }
-
- @JavaScriptBody(args = { "arr", "offset", "len" }, body =
- "var r = {};\n"
- + "r.charCodeAt = function(idx) { return arr[offset + idx]; };\n"
- + "return JSInflate.inflate(r);"
- )
- private static native Object infl(byte[] arr, int offset, int len);
-
- /**
- * Uncompresses bytes into specified buffer. Returns actual number
- * of bytes uncompressed. A return value of 0 indicates that
- * needsInput() or needsDictionary() should be called in order to
- * determine if more input data or a preset dictionary is required.
- * In the latter case, getAdler() can be used to get the Adler-32
- * value of the dictionary required.
- * @param b the buffer for the uncompressed data
- * @return the actual number of uncompressed bytes
- * @exception DataFormatException if the compressed data format is invalid
- * @see Inflater#needsInput
- * @see Inflater#needsDictionary
- */
- public int inflate(byte[] b) throws DataFormatException {
- return inflate(b, 0, b.length);
- }
-
- /**
- * Returns the ADLER-32 value of the uncompressed data.
- * @return the ADLER-32 value of the uncompressed data
- */
- public int getAdler() {
- return 0;
- }
-
- /**
- * Returns the total number of compressed bytes input so far.
- *
- * Since the number of bytes may be greater than
- * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
- * the preferred means of obtaining this information.Adler32
class.
+ * The checksum starts off with a value of 1.
+ */
+ public Adler32 ()
+ {
+ reset();
+ }
+
+ /**
+ * Resets the Adler32 checksum to the initial value.
+ */
+ public void reset ()
+ {
+ checksum = 1; //Initialize to 1
+ }
+
+ /**
+ * Updates the checksum with the byte b.
+ *
+ * @param bval the data value to add. The high byte of the int is ignored.
+ */
+ public void update (int bval)
+ {
+ //We could make a length 1 byte array and call update again, but I
+ //would rather not have that overhead
+ int s1 = checksum & 0xffff;
+ int s2 = checksum >>> 16;
+
+ s1 = (s1 + (bval & 0xFF)) % BASE;
+ s2 = (s1 + s2) % BASE;
+
+ checksum = (s2 << 16) + s1;
+ }
+
+ /**
+ * Updates the checksum with the bytes taken from the array.
+ *
+ * @param buffer an array of bytes
+ */
+ public void update (byte[] buffer)
+ {
+ update(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Updates the checksum with the bytes taken from the array.
+ *
+ * @param buf an array of bytes
+ * @param off the start of the data used for this update
+ * @param len the number of bytes to use for this update
+ */
+ public void update (byte[] buf, int off, int len)
+ {
+ //(By Per Bothner)
+ int s1 = checksum & 0xffff;
+ int s2 = checksum >>> 16;
+
+ while (len > 0)
+ {
+ // We can defer the modulo operation:
+ // s1 maximally grows from 65521 to 65521 + 255 * 3800
+ // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
+ int n = 3800;
+ if (n > len)
+ n = len;
+ len -= n;
+ while (--n >= 0)
+ {
+ s1 = s1 + (buf[off++] & 0xFF);
+ s2 = s2 + s1;
+ }
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+
+ /*Old implementation, borrowed from somewhere:
+ int n;
+
+ while (len-- > 0) {
+
+ s1 = (s1 + (bs[offset++] & 0xff)) % BASE;
+ s2 = (s2 + s1) % BASE;
+ }*/
+
+ checksum = (s2 << 16) | s1;
+ }
+
+ /**
+ * Returns the Adler32 data checksum computed so far.
+ */
+ public long getValue()
+ {
+ return (long) checksum & 0xffffffffL;
+ }
+}
diff -r 960ecf7cea5d -r 693745d01b55 emul/mini/src/main/java/java/util/zip/CRC32.java
--- a/emul/mini/src/main/java/java/util/zip/CRC32.java Fri Feb 01 15:19:16 2013 +0100
+++ b/emul/mini/src/main/java/java/util/zip/CRC32.java Fri Feb 01 18:02:16 2013 +0100
@@ -1,113 +1,132 @@
-/*
- * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
+/* CRC32.java - Computes CRC32 data checksum of a data stream
+ Copyright (C) 1999. 2000, 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
package java.util.zip;
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * The actual CRC32 algorithm is taken from RFC 1952.
+ * Status: Believed complete and correct.
+ */
+
/**
- * A class that can be used to compute the CRC-32 of a data stream.
+ * Computes CRC32 data checksum of a data stream.
+ * The actual CRC32 algorithm is described in RFC 1952
+ * (GZIP file format specification version 4.3).
+ * Can be used to get the CRC32 over a stream if used with checked input/output
+ * streams.
*
- * @see Checksum
- * @author David Connelly
+ * @see InflaterInputStream
+ * @see DeflaterOutputStream
+ *
+ * @author Per Bothner
+ * @date April 1, 1999.
*/
-public
-class CRC32 implements Checksum {
- private int crc = 0xFFFFFFFF;
+public class CRC32 implements Checksum
+{
+ /** The crc data checksum so far. */
+ private int crc = 0;
- /**
- * Creates a new CRC32 object.
- */
- public CRC32() {
- }
+ /** The fast CRC table. Computed once when the CRC32 class is loaded. */
+ private static int[] crc_table = make_crc_table();
+ /** Make the table for a fast CRC. */
+ private static int[] make_crc_table ()
+ {
+ int[] crc_table = new int[256];
+ for (int n = 0; n < 256; n++)
+ {
+ int c = n;
+ for (int k = 8; --k >= 0; )
+ {
+ if ((c & 1) != 0)
+ c = 0xedb88320 ^ (c >>> 1);
+ else
+ c = c >>> 1;
+ }
+ crc_table[n] = c;
+ }
+ return crc_table;
+ }
- /**
- * Updates the CRC-32 checksum with the specified byte (the low
- * eight bits of the argument b).
- *
- * @param b the byte to update the checksum with
- */
- public void update(int b) {
- byte[] arr = { (byte)b };
- update(arr);
- }
+ /**
+ * Returns the CRC32 data checksum computed so far.
+ */
+ public long getValue ()
+ {
+ return (long) crc & 0xffffffffL;
+ }
- /**
- * Updates the CRC-32 checksum with the specified array of bytes.
- */
- public void update(byte[] b, int off, int len) {
- if (b == null) {
- throw new NullPointerException();
- }
- if (off < 0 || len < 0 || off > b.length - len) {
- throw new ArrayIndexOutOfBoundsException();
- }
- crc = updateBytes(crc, b, off, len);
- }
+ /**
+ * Resets the CRC32 data checksum as if no update was ever called.
+ */
+ public void reset () { crc = 0; }
- /**
- * Updates the CRC-32 checksum with the specified array of bytes.
- *
- * @param b the array of bytes to update the checksum with
- */
- public void update(byte[] b) {
- crc = updateBytes(crc, b, 0, b.length);
- }
+ /**
+ * Updates the checksum with the int bval.
+ *
+ * @param bval (the byte is taken as the lower 8 bits of bval)
+ */
- /**
- * Resets CRC-32 to initial value.
- */
- public void reset() {
- crc = 0;
- }
+ public void update (int bval)
+ {
+ int c = ~crc;
+ c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8);
+ crc = ~c;
+ }
- /**
- * Returns CRC-32 value.
- */
- public long getValue() {
- return (long)crc & 0xffffffffL;
- }
+ /**
+ * Adds the byte array to the data checksum.
+ *
+ * @param buf the buffer which contains the data
+ * @param off the offset in the buffer where the data starts
+ * @param len the length of the data
+ */
+ public void update (byte[] buf, int off, int len)
+ {
+ int c = ~crc;
+ while (--len >= 0)
+ c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8);
+ crc = ~c;
+ }
- // XXX: taken from
- // http://introcs.cs.princeton.edu/java/51data/CRC32.java.html
- private static int updateBytes(int crc, byte[] arr, int off, int len) {
- int poly = 0xEDB88320; // reverse polynomial
-
- while (len-- > 0) {
- byte b = arr[off++];
- int temp = (crc ^ b) & 0xff;
-
- // read 8 bits one at a time
- for (int i = 0; i < 8; i++) {
- if ((temp & 1) == 1) {
- temp = (temp >>> 1) ^ poly;
- } else {
- temp = (temp >>> 1);
- }
- }
- crc = (crc >>> 8) ^ temp;
- }
- return crc ^ 0xffffffff;
- }
+ /**
+ * Adds the complete byte array to the data checksum.
+ */
+ public void update (byte[] buf) { update(buf, 0, buf.length); }
}
diff -r 960ecf7cea5d -r 693745d01b55 emul/mini/src/main/java/java/util/zip/Inflater.java
--- a/emul/mini/src/main/java/java/util/zip/Inflater.java Fri Feb 01 15:19:16 2013 +0100
+++ b/emul/mini/src/main/java/java/util/zip/Inflater.java Fri Feb 01 18:02:16 2013 +0100
@@ -1,32 +1,43 @@
-/*
- * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
+/* Inflater.java - Decompress a data stream
+ Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
package java.util.zip;
-import org.apidesign.bck2brwsr.core.ExtraJavaScript;
-import org.apidesign.bck2brwsr.core.JavaScriptBody;
+import org.apidesign.bck2brwsr.emul.lang.System;
/**
* This class provides support for general purpose decompression using the
@@ -73,271 +84,1392 @@
* @author David Connelly
*
*/
-@ExtraJavaScript(
- resource = "/org/apidesign/vm4brwsr/emul/zip/js-inflate.min.js"
-)
-public
-class Inflater {
- private String data = "";
- private int offset;
- private long counter;
- private boolean finished;
- private boolean needDict;
- private static final byte[] defaultBuf = new byte[0];
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+/**
+ * Inflater is used to decompress data that has been compressed according
+ * to the "deflate" standard described in rfc1950.
+ *
+ * The usage is as following. First you have to set some input with
+ * setInput()
, then inflate() it. If inflate doesn't
+ * inflate any bytes there may be three reasons:
+ *
+ *
+ * Once the first output byte is produced, a dictionary will not be
+ * needed at a later stage.
+ *
+ * @author John Leuner, Jochen Hoenicke
+ * @author Tom Tromey
+ * @date May 17, 1999
+ * @since JDK 1.1
+ */
+public class Inflater
+{
+ /* Copy lengths for literal codes 257..285 */
+ private static final int CPLENS[] =
+ {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
+ };
+
+ /* Extra bits for literal codes 257..285 */
+ private static final int CPLEXT[] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
+ };
+
+ /* Copy offsets for distance codes 0..29 */
+ private static final int CPDIST[] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577
+ };
+
+ /* Extra bits for distance codes */
+ private static final int CPDEXT[] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13
+ };
+
+ /* This are the state in which the inflater can be. */
+ private static final int DECODE_HEADER = 0;
+ private static final int DECODE_DICT = 1;
+ private static final int DECODE_BLOCKS = 2;
+ private static final int DECODE_STORED_LEN1 = 3;
+ private static final int DECODE_STORED_LEN2 = 4;
+ private static final int DECODE_STORED = 5;
+ private static final int DECODE_DYN_HEADER = 6;
+ private static final int DECODE_HUFFMAN = 7;
+ private static final int DECODE_HUFFMAN_LENBITS = 8;
+ private static final int DECODE_HUFFMAN_DIST = 9;
+ private static final int DECODE_HUFFMAN_DISTBITS = 10;
+ private static final int DECODE_CHKSUM = 11;
+ private static final int FINISHED = 12;
+
+ /** This variable contains the current state. */
+ private int mode;
+
+ /**
+ * The adler checksum of the dictionary or of the decompressed
+ * stream, as it is written in the header resp. footer of the
+ * compressed stream. setInput()
.
+ * NOTE: needsInput() also returns true when, the stream is finished.
+ * setDictionary()
.
+ *
+ * Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
+ */
+ private int readAdler;
+ /**
+ * The number of bits needed to complete the current state. This
+ * is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
+ * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.
+ */
+ private int neededBits;
+ private int repLength, repDist;
+ private int uncomprLen;
+ /**
+ * True, if the last block flag was set in the last block of the
+ * inflated stream. This means that the stream ends after the
+ * current block.
+ */
+ private boolean isLastBlock;
+
+ /**
+ * The total number of inflated bytes.
+ */
+ private long totalOut;
+ /**
+ * The total number of bytes set with setInput(). This is not the
+ * value returned by getTotalIn(), since this also includes the
+ * unprocessed input.
+ */
+ private long totalIn;
+ /**
+ * This variable stores the nowrap flag that was given to the constructor.
+ * True means, that the inflated stream doesn't contain a header nor the
+ * checksum in the footer.
+ */
+ private boolean nowrap;
+
+ private StreamManipulator input;
+ private OutputWindow outputWindow;
+ private InflaterDynHeader dynHeader;
+ private InflaterHuffmanTree litlenTree, distTree;
+ private Adler32 adler;
+
+ /**
+ * Creates a new inflater.
+ */
+ public Inflater ()
+ {
+ this (false);
+ }
+
+ /**
+ * Creates a new inflater.
+ * @param nowrap true if no header and checksum field appears in the
+ * stream. This is used for GZIPed input. For compatibility with
+ * Sun JDK you should provide one byte of input more than needed in
+ * this case.
+ */
+ public Inflater (boolean nowrap)
+ {
+ this.nowrap = nowrap;
+ this.adler = new Adler32();
+ input = new StreamManipulator();
+ outputWindow = new OutputWindow();
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ }
+
+ /**
+ * Finalizes this object.
+ */
+ protected void finalize ()
+ {
+ /* Exists only for compatibility */
+ }
+
+ /**
+ * Frees all objects allocated by the inflater. There's no reason
+ * to call this, since you can just rely on garbage collection (even
+ * for the Sun implementation). Exists only for compatibility
+ * with Sun's JDK, where the compressor allocates native memory.
+ * If you call any method (even reset) afterwards the behaviour is
+ * undefined.
+ * @deprecated Just clear all references to inflater instead.
+ */
+ public void end ()
+ {
+ outputWindow = null;
+ input = null;
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ adler = null;
+ }
+
+ /**
+ * Returns true, if the inflater has finished. This means, that no
+ * input is needed and no output can be produced.
+ */
+ public boolean finished()
+ {
+ return mode == FINISHED && outputWindow.getAvailable() == 0;
+ }
+
+ /**
+ * Gets the adler checksum. This is either the checksum of all
+ * uncompressed bytes returned by inflate(), or if needsDictionary()
+ * returns true (and thus no output was yet produced) this is the
+ * adler checksum of the expected dictionary.
+ * @returns the adler checksum.
+ */
+ public int getAdler()
+ {
+ return needsDictionary() ? readAdler : (int) adler.getValue();
+ }
+
+ /**
+ * Gets the number of unprocessed input. Useful, if the end of the
+ * stream is reached and you want to further process the bytes after
+ * the deflate stream.
+ * @return the number of bytes of the input which were not processed.
+ */
+ public int getRemaining()
+ {
+ return input.getAvailableBytes();
+ }
+
+ /**
+ * Gets the total number of processed compressed input bytes.
+ * @return the total number of bytes of processed input bytes.
+ */
+ public int getTotalIn()
+ {
+ return (int)getBytesRead();
+ }
+
+ /**
+ * Gets the total number of output bytes returned by inflate().
+ * @return the total number of output bytes.
+ */
+ public int getTotalOut()
+ {
+ return (int)totalOut;
+ }
+
+ public long getBytesWritten() {
+ return totalOut;
+ }
+
+ public long getBytesRead() {
+ return totalIn - getRemaining();
+ }
+
+
+ /**
+ * Inflates the compressed stream to the output buffer. If this
+ * returns 0, you should check, whether needsDictionary(),
+ * needsInput() or finished() returns true, to determine why no
+ * further output is produced.
+ * @param buffer the output buffer.
+ * @return the number of bytes written to the buffer, 0 if no further
+ * output can be produced.
+ * @exception DataFormatException if deflated stream is invalid.
+ * @exception IllegalArgumentException if buf has length 0.
+ */
+ public int inflate (byte[] buf) throws DataFormatException
+ {
+ return inflate (buf, 0, buf.length);
+ }
+
+ /**
+ * Inflates the compressed stream to the output buffer. If this
+ * returns 0, you should check, whether needsDictionary(),
+ * needsInput() or finished() returns true, to determine why no
+ * further output is produced.
+ * @param buffer the output buffer.
+ * @param off the offset into buffer where the output should start.
+ * @param len the maximum length of the output.
+ * @return the number of bytes written to the buffer, 0 if no further
+ * output can be produced.
+ * @exception DataFormatException if deflated stream is invalid.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public int inflate (byte[] buf, int off, int len) throws DataFormatException
+ {
+ /* Special case: len may be zero */
+ if (len == 0)
+ return 0;
+ /* Check for correct buff, off, len triple */
+ if (0 > off || off > off + len || off + len > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+ int count = 0;
+ int more;
+ do
+ {
+ if (mode != DECODE_CHKSUM)
+ {
+ /* Don't give away any output, if we are waiting for the
+ * checksum in the input stream.
+ *
+ * With this trick we have always:
+ * needsInput() and not finished()
+ * implies more output can be produced.
+ */
+ more = outputWindow.copyOutput(buf, off, len);
+ adler.update(buf, off, more);
+ off += more;
+ count += more;
+ totalOut += more;
+ len -= more;
+ if (len == 0)
+ return count;
+ }
+ }
+ while (decode() || (outputWindow.getAvailable() > 0
+ && mode != DECODE_CHKSUM));
+ return count;
+ }
+
+ /**
+ * Returns true, if a preset dictionary is needed to inflate the input.
+ */
+ public boolean needsDictionary ()
+ {
+ return mode == DECODE_DICT && neededBits == 0;
+ }
+
+ /**
+ * Returns true, if the input buffer is empty.
+ * You should then call setInput().
+ *
+ * NOTE: This method also returns true when the stream is finished.
+ */
+ public boolean needsInput ()
+ {
+ return input.needsInput ();
+ }
+
+ /**
+ * Resets the inflater so that a new stream can be decompressed. All
+ * pending input and output will be discarded.
+ */
+ public void reset ()
+ {
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ totalIn = totalOut = 0;
+ input.reset();
+ outputWindow.reset();
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ isLastBlock = false;
+ adler.reset();
+ }
+
+ /**
+ * Sets the preset dictionary. This should only be called, if
+ * needsDictionary() returns true and it should set the same
+ * dictionary, that was used for deflating. The getAdler()
+ * function returns the checksum of the dictionary needed.
+ * @param buffer the dictionary.
+ * @exception IllegalStateException if no dictionary is needed.
+ * @exception IllegalArgumentException if the dictionary checksum is
+ * wrong.
+ */
+ public void setDictionary (byte[] buffer)
+ {
+ setDictionary(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Sets the preset dictionary. This should only be called, if
+ * needsDictionary() returns true and it should set the same
+ * dictionary, that was used for deflating. The getAdler()
+ * function returns the checksum of the dictionary needed.
+ * @param buffer the dictionary.
+ * @param off the offset into buffer where the dictionary starts.
+ * @param len the length of the dictionary.
+ * @exception IllegalStateException if no dictionary is needed.
+ * @exception IllegalArgumentException if the dictionary checksum is
+ * wrong.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public void setDictionary (byte[] buffer, int off, int len)
+ {
+ if (!needsDictionary())
+ throw new IllegalStateException();
+
+ adler.update(buffer, off, len);
+ if ((int) adler.getValue() != readAdler)
+ throw new IllegalArgumentException("Wrong adler checksum");
+ adler.reset();
+ outputWindow.copyDict(buffer, off, len);
+ mode = DECODE_BLOCKS;
+ }
+
+ /**
+ * Sets the input. This should only be called, if needsInput()
+ * returns true.
+ * @param buffer the input.
+ * @exception IllegalStateException if no input is needed.
+ */
+ public void setInput (byte[] buf)
+ {
+ setInput (buf, 0, buf.length);
+ }
+
+ /**
+ * Sets the input. This should only be called, if needsInput()
+ * returns true.
+ * @param buffer the input.
+ * @param off the offset into buffer where the input starts.
+ * @param len the length of the input.
+ * @exception IllegalStateException if no input is needed.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public void setInput (byte[] buf, int off, int len)
+ {
+ input.setInput (buf, off, len);
+ totalIn += len;
+ }
+ private static final int DEFLATED = 8;
+ /**
+ * Decodes the deflate header.
+ * @return false if more input is needed.
+ * @exception DataFormatException if header is invalid.
+ */
+ private boolean decodeHeader () throws DataFormatException
+ {
+ int header = input.peekBits(16);
+ if (header < 0)
+ return false;
+ input.dropBits(16);
+
+ /* The header is written in "wrong" byte order */
+ header = ((header << 8) | (header >> 8)) & 0xffff;
+ if (header % 31 != 0)
+ throw new DataFormatException("Header checksum illegal");
+
+ if ((header & 0x0f00) != (DEFLATED << 8))
+ throw new DataFormatException("Compression Method unknown");
+
+ /* Maximum size of the backwards window in bits.
+ * We currently ignore this, but we could use it to make the
+ * inflater window more space efficient. On the other hand the
+ * full window (15 bits) is needed most times, anyway.
+ int max_wbits = ((header & 0x7000) >> 12) + 8;
+ */
+
+ if ((header & 0x0020) == 0) // Dictionary flag?
+ {
+ mode = DECODE_BLOCKS;
+ }
+ else
+ {
+ mode = DECODE_DICT;
+ neededBits = 32;
+ }
+ return true;
+ }
+
+ /**
+ * Decodes the dictionary checksum after the deflate header.
+ * @return false if more input is needed.
+ */
+ private boolean decodeDict ()
+ {
+ while (neededBits > 0)
+ {
+ int dictByte = input.peekBits(8);
+ if (dictByte < 0)
+ return false;
+ input.dropBits(8);
+ readAdler = (readAdler << 8) | dictByte;
+ neededBits -= 8;
+ }
+ return false;
+ }
+
+ /**
+ * Decodes the huffman encoded symbols in the input stream.
+ * @return false if more input is needed, true if output window is
+ * full or the current block ends.
+ * @exception DataFormatException if deflated stream is invalid.
+ */
+ private boolean decodeHuffman () throws DataFormatException
+ {
+ int free = outputWindow.getFreeSpace();
+ while (free >= 258)
+ {
+ int symbol;
+ switch (mode)
+ {
+ case DECODE_HUFFMAN:
+ /* This is the inner loop so it is optimized a bit */
+ while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0)
+ {
+ outputWindow.write(symbol);
+ if (--free < 258)
+ return true;
+ }
+ if (symbol < 257)
+ {
+ if (symbol < 0)
+ return false;
+ else
+ {
+ /* symbol == 256: end of block */
+ distTree = null;
+ litlenTree = null;
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ }
+
+ try
+ {
+ repLength = CPLENS[symbol - 257];
+ neededBits = CPLEXT[symbol - 257];
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ throw new DataFormatException("Illegal rep length code");
+ }
+ /* fall through */
+ case DECODE_HUFFMAN_LENBITS:
+ if (neededBits > 0)
+ {
+ mode = DECODE_HUFFMAN_LENBITS;
+ int i = input.peekBits(neededBits);
+ if (i < 0)
+ return false;
+ input.dropBits(neededBits);
+ repLength += i;
+ }
+ mode = DECODE_HUFFMAN_DIST;
+ /* fall through */
+ case DECODE_HUFFMAN_DIST:
+ symbol = distTree.getSymbol(input);
+ if (symbol < 0)
+ return false;
+ try
+ {
+ repDist = CPDIST[symbol];
+ neededBits = CPDEXT[symbol];
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ throw new DataFormatException("Illegal rep dist code");
+ }
+ /* fall through */
+ case DECODE_HUFFMAN_DISTBITS:
+ if (neededBits > 0)
+ {
+ mode = DECODE_HUFFMAN_DISTBITS;
+ int i = input.peekBits(neededBits);
+ if (i < 0)
+ return false;
+ input.dropBits(neededBits);
+ repDist += i;
+ }
+ outputWindow.repeat(repLength, repDist);
+ free -= repLength;
+ mode = DECODE_HUFFMAN;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Decodes the adler checksum after the deflate stream.
+ * @return false if more input is needed.
+ * @exception DataFormatException if checksum doesn't match.
+ */
+ private boolean decodeChksum () throws DataFormatException
+ {
+ while (neededBits > 0)
+ {
+ int chkByte = input.peekBits(8);
+ if (chkByte < 0)
+ return false;
+ input.dropBits(8);
+ readAdler = (readAdler << 8) | chkByte;
+ neededBits -= 8;
+ }
+ if ((int) adler.getValue() != readAdler)
+ throw new DataFormatException("Adler chksum doesn't match: "
+ +Integer.toHexString((int)adler.getValue())
+ +" vs. "+Integer.toHexString(readAdler));
+ mode = FINISHED;
+ return false;
+ }
+
+ /**
+ * Decodes the deflated stream.
+ * @return false if more input is needed, or if finished.
+ * @exception DataFormatException if deflated stream is invalid.
+ */
+ private boolean decode () throws DataFormatException
+ {
+ switch (mode)
+ {
+ case DECODE_HEADER:
+ return decodeHeader();
+ case DECODE_DICT:
+ return decodeDict();
+ case DECODE_CHKSUM:
+ return decodeChksum();
+
+ case DECODE_BLOCKS:
+ if (isLastBlock)
+ {
+ if (nowrap)
+ {
+ mode = FINISHED;
+ return false;
+ }
+ else
+ {
+ input.skipToByteBoundary();
+ neededBits = 32;
+ mode = DECODE_CHKSUM;
+ return true;
+ }
+ }
+
+ int type = input.peekBits(3);
+ if (type < 0)
+ return false;
+ input.dropBits(3);
+
+ if ((type & 1) != 0)
+ isLastBlock = true;
+ switch (type >> 1)
+ {
+ case DeflaterConstants.STORED_BLOCK:
+ input.skipToByteBoundary();
+ mode = DECODE_STORED_LEN1;
+ break;
+ case DeflaterConstants.STATIC_TREES:
+ litlenTree = InflaterHuffmanTree.defLitLenTree;
+ distTree = InflaterHuffmanTree.defDistTree;
+ mode = DECODE_HUFFMAN;
+ break;
+ case DeflaterConstants.DYN_TREES:
+ dynHeader = new InflaterDynHeader();
+ mode = DECODE_DYN_HEADER;
+ break;
+ default:
+ throw new DataFormatException("Unknown block type "+type);
+ }
+ return true;
+
+ case DECODE_STORED_LEN1:
+ {
+ if ((uncomprLen = input.peekBits(16)) < 0)
+ return false;
+ input.dropBits(16);
+ mode = DECODE_STORED_LEN2;
+ }
+ /* fall through */
+ case DECODE_STORED_LEN2:
+ {
+ int nlen = input.peekBits(16);
+ if (nlen < 0)
+ return false;
+ input.dropBits(16);
+ if (nlen != (uncomprLen ^ 0xffff))
+ throw new DataFormatException("broken uncompressed block");
+ mode = DECODE_STORED;
+ }
+ /* fall through */
+ case DECODE_STORED:
+ {
+ int more = outputWindow.copyStored(input, uncomprLen);
+ uncomprLen -= more;
+ if (uncomprLen == 0)
+ {
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ return !input.needsInput();
+ }
+
+ case DECODE_DYN_HEADER:
+ if (!dynHeader.decode(input))
+ return false;
+ litlenTree = dynHeader.buildLitLenTree();
+ distTree = dynHeader.buildDistTree();
+ mode = DECODE_HUFFMAN;
+ /* fall through */
+ case DECODE_HUFFMAN:
+ case DECODE_HUFFMAN_LENBITS:
+ case DECODE_HUFFMAN_DIST:
+ case DECODE_HUFFMAN_DISTBITS:
+ return decodeHuffman();
+ case FINISHED:
+ return false;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+
+ interface DeflaterConstants {
+ final static boolean DEBUGGING = false;
+
+ final static int STORED_BLOCK = 0;
+ final static int STATIC_TREES = 1;
+ final static int DYN_TREES = 2;
+ final static int PRESET_DICT = 0x20;
+
+ final static int DEFAULT_MEM_LEVEL = 8;
+
+ final static int MAX_MATCH = 258;
+ final static int MIN_MATCH = 3;
+
+ final static int MAX_WBITS = 15;
+ final static int WSIZE = 1 << MAX_WBITS;
+ final static int WMASK = WSIZE - 1;
+
+ final static int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
+ final static int HASH_SIZE = 1 << HASH_BITS;
+ final static int HASH_MASK = HASH_SIZE - 1;
+ final static int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
+
+ final static int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
+ final static int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
+
+ final static int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
+ final static int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5);
+
+ final static int DEFLATE_STORED = 0;
+ final static int DEFLATE_FAST = 1;
+ final static int DEFLATE_SLOW = 2;
+
+ final static int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 };
+ final static int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 };
+ final static int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 };
+ final static int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 };
+ final static int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 };
+ }
+ private static class InflaterHuffmanTree {
+ private final static int MAX_BITLEN = 15;
+ private short[] tree;
+
+ public static InflaterHuffmanTree defLitLenTree, defDistTree;
+
+ static
+ {
+ try
+ {
+ byte[] codeLengths = new byte[288];
+ int i = 0;
+ while (i < 144)
+ codeLengths[i++] = 8;
+ while (i < 256)
+ codeLengths[i++] = 9;
+ while (i < 280)
+ codeLengths[i++] = 7;
+ while (i < 288)
+ codeLengths[i++] = 8;
+ defLitLenTree = new InflaterHuffmanTree(codeLengths);
+
+ codeLengths = new byte[32];
+ i = 0;
+ while (i < 32)
+ codeLengths[i++] = 5;
+ defDistTree = new InflaterHuffmanTree(codeLengths);
+ }
+ catch (DataFormatException ex)
+ {
+ throw new IllegalStateException
+ ("InflaterHuffmanTree: static tree length illegal");
+ }
+ }
+
+ /**
+ * Constructs a Huffman tree from the array of code lengths.
+ *
+ * @param codeLengths the array of code lengths
+ */
+ public InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException
+ {
+ buildTree(codeLengths);
+ }
+
+ private void buildTree(byte[] codeLengths) throws DataFormatException
+ {
+ int[] blCount = new int[MAX_BITLEN+1];
+ int[] nextCode = new int[MAX_BITLEN+1];
+ for (int i = 0; i < codeLengths.length; i++)
+ {
+ int bits = codeLengths[i];
+ if (bits > 0)
+ blCount[bits]++;
+ }
+
+ int code = 0;
+ int treeSize = 512;
+ for (int bits = 1; bits <= MAX_BITLEN; bits++)
+ {
+ nextCode[bits] = code;
+ code += blCount[bits] << (16 - bits);
+ if (bits >= 10)
+ {
+ /* We need an extra table for bit lengths >= 10. */
+ int start = nextCode[bits] & 0x1ff80;
+ int end = code & 0x1ff80;
+ treeSize += (end - start) >> (16 - bits);
+ }
+ }
+ if (code != 65536)
+ throw new DataFormatException("Code lengths don't add up properly.");
+
+ /* Now create and fill the extra tables from longest to shortest
+ * bit len. This way the sub trees will be aligned.
+ */
+ tree = new short[treeSize];
+ int treePtr = 512;
+ for (int bits = MAX_BITLEN; bits >= 10; bits--)
+ {
+ int end = code & 0x1ff80;
+ code -= blCount[bits] << (16 - bits);
+ int start = code & 0x1ff80;
+ for (int i = start; i < end; i += 1 << 7)
+ {
+ tree[bitReverse(i)]
+ = (short) ((-treePtr << 4) | bits);
+ treePtr += 1 << (bits-9);
+ }
+ }
+
+ for (int i = 0; i < codeLengths.length; i++)
+ {
+ int bits = codeLengths[i];
+ if (bits == 0)
+ continue;
+ code = nextCode[bits];
+ int revcode = bitReverse(code);
+ if (bits <= 9)
+ {
+ do
+ {
+ tree[revcode] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ }
+ while (revcode < 512);
+ }
+ else
+ {
+ int subTree = tree[revcode & 511];
+ int treeLen = 1 << (subTree & 15);
+ subTree = -(subTree >> 4);
+ do
+ {
+ tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ }
+ while (revcode < treeLen);
+ }
+ nextCode[bits] = code + (1 << (16 - bits));
+ }
+ }
+ private final static String bit4Reverse =
+ "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017";
+ static short bitReverse(int value) {
+ return (short) (bit4Reverse.charAt(value & 0xf) << 12
+ | bit4Reverse.charAt((value >> 4) & 0xf) << 8
+ | bit4Reverse.charAt((value >> 8) & 0xf) << 4
+ | bit4Reverse.charAt(value >> 12));
+ }
+
+ /**
+ * Reads the next symbol from input. The symbol is encoded using the
+ * huffman tree.
+ * @param input the input source.
+ * @return the next symbol, or -1 if not enough input is available.
+ */
+ public int getSymbol(StreamManipulator input) throws DataFormatException
+ {
+ int lookahead, symbol;
+ if ((lookahead = input.peekBits(9)) >= 0)
+ {
+ if ((symbol = tree[lookahead]) >= 0)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ int subtree = -(symbol >> 4);
+ int bitlen = symbol & 15;
+ if ((lookahead = input.peekBits(bitlen)) >= 0)
+ {
+ symbol = tree[subtree | (lookahead >> 9)];
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ {
+ int bits = input.getAvailableBits();
+ lookahead = input.peekBits(bits);
+ symbol = tree[subtree | (lookahead >> 9)];
+ if ((symbol & 15) <= bits)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ return -1;
+ }
+ }
+ else
+ {
+ int bits = input.getAvailableBits();
+ lookahead = input.peekBits(bits);
+ symbol = tree[lookahead];
+ if (symbol >= 0 && (symbol & 15) <= bits)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ return -1;
+ }
+ }
+ }
+ private static class InflaterDynHeader
+ {
+ private static final int LNUM = 0;
+ private static final int DNUM = 1;
+ private static final int BLNUM = 2;
+ private static final int BLLENS = 3;
+ private static final int LENS = 4;
+ private static final int REPS = 5;
+
+ private static final int repMin[] = { 3, 3, 11 };
+ private static final int repBits[] = { 2, 3, 7 };
+
+
+ private byte[] blLens;
+ private byte[] litdistLens;
+
+ private InflaterHuffmanTree blTree;
+
+ private int mode;
+ private int lnum, dnum, blnum, num;
+ private int repSymbol;
+ private byte lastLen;
+ private int ptr;
+
+ private static final int[] BL_ORDER =
+ { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ public InflaterDynHeader()
+ {
+ }
+
+ public boolean decode(StreamManipulator input) throws DataFormatException
+ {
+ decode_loop:
+ for (;;)
+ {
+ switch (mode)
+ {
+ case LNUM:
+ lnum = input.peekBits(5);
+ if (lnum < 0)
+ return false;
+ lnum += 257;
+ input.dropBits(5);
+ // System.err.println("LNUM: "+lnum);
+ mode = DNUM;
+ /* fall through */
+ case DNUM:
+ dnum = input.peekBits(5);
+ if (dnum < 0)
+ return false;
+ dnum++;
+ input.dropBits(5);
+ // System.err.println("DNUM: "+dnum);
+ num = lnum+dnum;
+ litdistLens = new byte[num];
+ mode = BLNUM;
+ /* fall through */
+ case BLNUM:
+ blnum = input.peekBits(4);
+ if (blnum < 0)
+ return false;
+ blnum += 4;
+ input.dropBits(4);
+ blLens = new byte[19];
+ ptr = 0;
+ // System.err.println("BLNUM: "+blnum);
+ mode = BLLENS;
+ /* fall through */
+ case BLLENS:
+ while (ptr < blnum)
+ {
+ int len = input.peekBits(3);
+ if (len < 0)
+ return false;
+ input.dropBits(3);
+ // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);
+ blLens[BL_ORDER[ptr]] = (byte) len;
+ ptr++;
+ }
+ blTree = new InflaterHuffmanTree(blLens);
+ blLens = null;
+ ptr = 0;
+ mode = LENS;
+ /* fall through */
+ case LENS:
+ {
+ int symbol;
+ while (((symbol = blTree.getSymbol(input)) & ~15) == 0)
+ {
+ /* Normal case: symbol in [0..15] */
+
+ // System.err.println("litdistLens["+ptr+"]: "+symbol);
+ litdistLens[ptr++] = lastLen = (byte) symbol;
+
+ if (ptr == num)
+ {
+ /* Finished */
+ return true;
+ }
+ }
+
+ /* need more input ? */
+ if (symbol < 0)
+ return false;
+
+ /* otherwise repeat code */
+ if (symbol >= 17)
+ {
+ /* repeat zero */
+ // System.err.println("repeating zero");
+ lastLen = 0;
+ }
+ else
+ {
+ if (ptr == 0)
+ throw new DataFormatException();
+ }
+ repSymbol = symbol-16;
+ mode = REPS;
+ }
+ /* fall through */
+
+ case REPS:
+ {
+ int bits = repBits[repSymbol];
+ int count = input.peekBits(bits);
+ if (count < 0)
+ return false;
+ input.dropBits(bits);
+ count += repMin[repSymbol];
+ // System.err.println("litdistLens repeated: "+count);
+
+ if (ptr + count > num)
+ throw new DataFormatException();
+ while (count-- > 0)
+ litdistLens[ptr++] = lastLen;
+
+ if (ptr == num)
+ {
+ /* Finished */
+ return true;
+ }
+ }
+ mode = LENS;
+ continue decode_loop;
+ }
+ }
+ }
+
+ public InflaterHuffmanTree buildLitLenTree() throws DataFormatException
+ {
+ byte[] litlenLens = new byte[lnum];
+ System.arraycopy(litdistLens, 0, litlenLens, 0, lnum);
+ return new InflaterHuffmanTree(litlenLens);
+ }
+
+ public InflaterHuffmanTree buildDistTree() throws DataFormatException
+ {
+ byte[] distLens = new byte[dnum];
+ System.arraycopy(litdistLens, lnum, distLens, 0, dnum);
+ return new InflaterHuffmanTree(distLens);
+ }
+ }
/**
- * Creates a new decompressor. If the parameter 'nowrap' is true then
- * the ZLIB header and checksum fields will not be used. This provides
- * compatibility with the compression format used by both GZIP and PKZIP.
- *
Since the number of bytes may be greater than - * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now - * the preferred means of obtaining this information.
- * - * @return the total number of uncompressed bytes output so far - */ - public int getTotalOut() { - return (int) getBytesWritten(); - } - - /** - * Returns the total number of uncompressed bytes output so far. - * - * @return the total (non-negative) number of uncompressed bytes output so far - * @since 1.5 - */ - public long getBytesWritten() { - return counter; - } - - /** - * Resets inflater so that a new set of input data can be processed. - */ - public void reset() { - data = ""; - finished = false; - needDict = false; - offset = 0; - } - - /** - * Closes the decompressor and discards any unprocessed input. - * This method should be called when the decompressor is no longer - * being used, but will also be called automatically by the finalize() - * method. Once this method is called, the behavior of the Inflater - * object is undefined. - */ - public void end() { - } + } diff -r 960ecf7cea5d -r 693745d01b55 vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Fri Feb 01 15:19:16 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Fri Feb 01 18:02:16 2013 +0100 @@ -19,9 +19,13 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Objects; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.HttpResource; import org.apidesign.bck2brwsr.vmtest.VMTest; import org.testng.annotations.Factory; @@ -29,23 +33,29 @@ * * @author Jaroslav Tulach