commit
3aed980f5e
@ -0,0 +1,197 @@ |
|||||||
|
/* Adler32.java - Computes Adler32 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 me.ag2s.epublib.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 Adler32 algorithm is taken from RFC 1950. |
||||||
|
* Status: Believed complete and correct. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Computes Adler32 checksum for a stream of data. An Adler32 |
||||||
|
* checksum is not as reliable as a CRC32 checksum, but a lot faster to |
||||||
|
* compute. |
||||||
|
* <p> |
||||||
|
* The specification for Adler32 may be found in RFC 1950. |
||||||
|
* (ZLIB Compressed Data Format Specification version 3.3) |
||||||
|
* <p> |
||||||
|
* <p> |
||||||
|
* From that document: |
||||||
|
* <p> |
||||||
|
* "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. |
||||||
|
* <p> |
||||||
|
* 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." |
||||||
|
* <p> |
||||||
|
* "8.2. The Adler-32 algorithm |
||||||
|
* <p> |
||||||
|
* The Adler-32 algorithm is much faster than the CRC32 algorithm yet |
||||||
|
* still provides an extremely low probability of undetected errors. |
||||||
|
* <p> |
||||||
|
* 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.) |
||||||
|
* <p> |
||||||
|
* 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 |
||||||
|
* @see InflaterInputStream |
||||||
|
* @see DeflaterOutputStream |
||||||
|
* @since JDK 1.1 |
||||||
|
*/ |
||||||
|
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 <code>Adler32</code> 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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,368 @@ |
|||||||
|
package me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.net.Uri; |
||||||
|
import android.os.ParcelFileDescriptor; |
||||||
|
import android.system.ErrnoException; |
||||||
|
import android.system.OsConstants; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
|
||||||
|
import java.io.Closeable; |
||||||
|
import java.io.DataInput; |
||||||
|
import java.io.DataInputStream; |
||||||
|
import java.io.EOFException; |
||||||
|
import java.io.FileDescriptor; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.nio.ByteOrder; |
||||||
|
import java.nio.channels.FileChannel; |
||||||
|
|
||||||
|
public class AndroidRandomReadableFile implements DataInput, Closeable { |
||||||
|
private ParcelFileDescriptor pfd; |
||||||
|
private FileInputStream fis; |
||||||
|
private DataInputStream dis; |
||||||
|
public long pos = 0; |
||||||
|
|
||||||
|
public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException { |
||||||
|
try { |
||||||
|
pfd = context.getContentResolver().openFileDescriptor(treeUri, "r"); |
||||||
|
fis = new FileInputStream(pfd.getFileDescriptor()); |
||||||
|
dis = new DataInputStream(fis); |
||||||
|
} catch (FileNotFoundException e) { |
||||||
|
throw e; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final FileDescriptor getFD() { |
||||||
|
return pfd.getFileDescriptor(); |
||||||
|
} |
||||||
|
|
||||||
|
public final FileChannel getChannel() { |
||||||
|
if (fis == null || pos != getPos()) { |
||||||
|
fis = new FileInputStream(pfd.getFileDescriptor()); |
||||||
|
} |
||||||
|
return fis.getChannel(); |
||||||
|
} |
||||||
|
|
||||||
|
public final FileInputStream getFileInputStream() { |
||||||
|
if (fis == null || pos != getPos()) { |
||||||
|
fis = new FileInputStream(pfd.getFileDescriptor()); |
||||||
|
} |
||||||
|
return fis; |
||||||
|
} |
||||||
|
|
||||||
|
public int read() throws IOException { |
||||||
|
byte[] b = new byte[1]; |
||||||
|
return (read(b, 0, 1) != -1) ? b[0] & 0xff : -1; |
||||||
|
} |
||||||
|
|
||||||
|
public int read(byte[] b) { |
||||||
|
try { |
||||||
|
return read(b, 0, b.length); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException { |
||||||
|
try { |
||||||
|
return android.system.Os.read(pfd.getFileDescriptor(), b, off, len); |
||||||
|
} catch (Exception e) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void syncInputStream() { |
||||||
|
fis = new FileInputStream(pfd.getFileDescriptor()); |
||||||
|
dis = new DataInputStream(fis); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void seek(long pos) throws IOException { |
||||||
|
try { |
||||||
|
android.system.Os.lseek(pfd.getFileDescriptor(), pos, OsConstants.SEEK_SET); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new IOException(e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public long length() throws IOException { |
||||||
|
try { |
||||||
|
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_END); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new IOException(e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public long getPos() { |
||||||
|
try { |
||||||
|
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR); |
||||||
|
} catch (Exception e) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void readFully(byte[] b) throws IOException { |
||||||
|
try { |
||||||
|
android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length); |
||||||
|
} catch (ErrnoException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void readFully(byte[] b, int off, int len) throws IOException { |
||||||
|
try { |
||||||
|
android.system.Os.read(pfd.getFileDescriptor(), b, off, len); |
||||||
|
//syncInputStream();
|
||||||
|
} catch (ErrnoException e) { |
||||||
|
throw new IOException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int skipBytes(int n) throws IOException { |
||||||
|
try { |
||||||
|
byte[] b = new byte[n]; |
||||||
|
return android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length); |
||||||
|
} catch (Exception e) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean readBoolean() throws IOException { |
||||||
|
int ch = this.read(); |
||||||
|
if (ch < 0) |
||||||
|
throw new EOFException(); |
||||||
|
return (ch != 0); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public byte readByte() throws IOException { |
||||||
|
int ch = this.read(); |
||||||
|
if (ch < 0) |
||||||
|
throw new EOFException(); |
||||||
|
return (byte) (ch); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public int readUnsignedByte() throws IOException { |
||||||
|
int ch = this.read(); |
||||||
|
if (ch < 0) |
||||||
|
throw new EOFException(); |
||||||
|
return ch; |
||||||
|
} |
||||||
|
|
||||||
|
private final byte[] readBuffer = new byte[8]; |
||||||
|
|
||||||
|
@Override |
||||||
|
public short readShort() throws IOException { |
||||||
|
readFully(readBuffer, 0, 2); |
||||||
|
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int readUnsignedShort() throws IOException { |
||||||
|
readFully(readBuffer, 0, 2); |
||||||
|
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get() & 0xffff; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public char readChar() throws IOException { |
||||||
|
readFully(readBuffer, 0, 2); |
||||||
|
return (char) ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int readInt() throws IOException { |
||||||
|
readFully(readBuffer, 0, 4); |
||||||
|
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asIntBuffer().get(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long readLong() throws IOException { |
||||||
|
readFully(readBuffer, 0, 8); |
||||||
|
return (((long) readBuffer[0] << 56) + |
||||||
|
((long) (readBuffer[1] & 255) << 48) + |
||||||
|
((long) (readBuffer[2] & 255) << 40) + |
||||||
|
((long) (readBuffer[3] & 255) << 32) + |
||||||
|
((long) (readBuffer[4] & 255) << 24) + |
||||||
|
((readBuffer[5] & 255) << 16) + |
||||||
|
((readBuffer[6] & 255) << 8) + |
||||||
|
((readBuffer[7] & 255) << 0)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public float readFloat() throws IOException { |
||||||
|
return Float.intBitsToFloat(readInt()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public double readDouble() throws IOException { |
||||||
|
return Double.longBitsToDouble(readLong()); |
||||||
|
} |
||||||
|
|
||||||
|
private char[] lineBuffer; |
||||||
|
|
||||||
|
@Deprecated |
||||||
|
@Override |
||||||
|
public String readLine() throws IOException { |
||||||
|
char[] buf = lineBuffer; |
||||||
|
|
||||||
|
if (buf == null) { |
||||||
|
buf = lineBuffer = new char[128]; |
||||||
|
} |
||||||
|
|
||||||
|
int room = buf.length; |
||||||
|
int offset = 0; |
||||||
|
int c; |
||||||
|
|
||||||
|
loop: |
||||||
|
while (true) { |
||||||
|
switch (c = this.read()) { |
||||||
|
case -1: |
||||||
|
case '\n': |
||||||
|
break loop; |
||||||
|
|
||||||
|
case '\r': |
||||||
|
int c2 = this.read(); |
||||||
|
break loop; |
||||||
|
|
||||||
|
default: |
||||||
|
if (--room < 0) { |
||||||
|
buf = new char[offset + 128]; |
||||||
|
room = buf.length - offset - 1; |
||||||
|
System.arraycopy(lineBuffer, 0, buf, 0, offset); |
||||||
|
lineBuffer = buf; |
||||||
|
} |
||||||
|
buf[offset++] = (char) c; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if ((c == -1) && (offset == 0)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return String.copyValueOf(buf, 0, offset); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String readUTF() throws IOException { |
||||||
|
if (pos != getPos()) { |
||||||
|
syncInputStream(); |
||||||
|
} |
||||||
|
return dis.readUTF(); |
||||||
|
} |
||||||
|
|
||||||
|
// public final static String readUTF(DataInput in) throws IOException {
|
||||||
|
// int utflen = in.readUnsignedShort();
|
||||||
|
// byte[] bytearr = null;
|
||||||
|
// char[] chararr = null;
|
||||||
|
// if (in instanceof DataInputStream) {
|
||||||
|
// DataInputStream dis = (DataInputStream)in;
|
||||||
|
// if (dis.bytearr.length < utflen){
|
||||||
|
// dis.bytearr = new byte[utflen*2];
|
||||||
|
// dis.chararr = new char[utflen*2];
|
||||||
|
// }
|
||||||
|
// chararr = dis.chararr;
|
||||||
|
// bytearr = dis.bytearr;
|
||||||
|
// } else {
|
||||||
|
// bytearr = new byte[utflen];
|
||||||
|
// chararr = new char[utflen];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// int c, char2, char3;
|
||||||
|
// int count = 0;
|
||||||
|
// int chararr_count=0;
|
||||||
|
//
|
||||||
|
// in.readFully(bytearr, 0, utflen);
|
||||||
|
//
|
||||||
|
// while (count < utflen) {
|
||||||
|
// c = (int) bytearr[count] & 0xff;
|
||||||
|
// if (c > 127) break;
|
||||||
|
// count++;
|
||||||
|
// chararr[chararr_count++]=(char)c;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// while (count < utflen) {
|
||||||
|
// c = (int) bytearr[count] & 0xff;
|
||||||
|
// switch (c >> 4) {
|
||||||
|
// case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
|
||||||
|
// /* 0xxxxxxx*/
|
||||||
|
// count++;
|
||||||
|
// chararr[chararr_count++]=(char)c;
|
||||||
|
// break;
|
||||||
|
// case 12: case 13:
|
||||||
|
// /* 110x xxxx 10xx xxxx*/
|
||||||
|
// count += 2;
|
||||||
|
// if (count > utflen)
|
||||||
|
// throw new UTFDataFormatException(
|
||||||
|
// "malformed input: partial character at end");
|
||||||
|
// char2 = (int) bytearr[count-1];
|
||||||
|
// if ((char2 & 0xC0) != 0x80)
|
||||||
|
// throw new UTFDataFormatException(
|
||||||
|
// "malformed input around byte " + count);
|
||||||
|
// chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
|
||||||
|
// (char2 & 0x3F));
|
||||||
|
// break;
|
||||||
|
// case 14:
|
||||||
|
// /* 1110 xxxx 10xx xxxx 10xx xxxx */
|
||||||
|
// count += 3;
|
||||||
|
// if (count > utflen)
|
||||||
|
// throw new UTFDataFormatException(
|
||||||
|
// "malformed input: partial character at end");
|
||||||
|
// char2 = (int) bytearr[count-2];
|
||||||
|
// char3 = (int) bytearr[count-1];
|
||||||
|
// if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
|
||||||
|
// throw new UTFDataFormatException(
|
||||||
|
// "malformed input around byte " + (count-1));
|
||||||
|
// chararr[chararr_count++]=(char)(((c & 0x0F) << 12) |
|
||||||
|
// ((char2 & 0x3F) << 6) |
|
||||||
|
// ((char3 & 0x3F) << 0));
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// /* 10xx xxxx, 1111 xxxx */
|
||||||
|
// throw new UTFDataFormatException(
|
||||||
|
// "malformed input around byte " + count);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // The number of chars produced may be less than utflen
|
||||||
|
// return new String(chararr, 0, chararr_count);
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() throws IOException { |
||||||
|
try { |
||||||
|
dis.close(); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
fis.close(); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
try { |
||||||
|
pfd.close(); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
/* 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 me.ag2s.epublib.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. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* 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. |
||||||
|
* |
||||||
|
* @author Per Bothner |
||||||
|
* @date April 1, 1999. |
||||||
|
* @see InflaterInputStream |
||||||
|
* @see DeflaterOutputStream |
||||||
|
*/ |
||||||
|
public class CRC32 implements Checksum { |
||||||
|
/** |
||||||
|
* The crc data checksum so far. |
||||||
|
*/ |
||||||
|
private int crc = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* The fast CRC table. Computed once when the CRC32 class is loaded. |
||||||
|
*/ |
||||||
|
private static final 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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the CRC32 data checksum computed so far. |
||||||
|
*/ |
||||||
|
public long getValue() { |
||||||
|
return (long) crc & 0xffffffffL; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resets the CRC32 data checksum as if no update was ever called. |
||||||
|
*/ |
||||||
|
public void reset() { |
||||||
|
crc = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the checksum with the int bval. |
||||||
|
* |
||||||
|
* @param bval (the byte is taken as the lower 8 bits of bval) |
||||||
|
*/ |
||||||
|
|
||||||
|
public void update(int bval) { |
||||||
|
int c = ~crc; |
||||||
|
c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8); |
||||||
|
crc = ~c; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds the complete byte array to the data checksum. |
||||||
|
*/ |
||||||
|
public void update(byte[] buf) { |
||||||
|
update(buf, 0, buf.length); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
/* CheckedInputStream.java - Compute checksum of data being read |
||||||
|
Copyright (C) 1999, 2000 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.FilterInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/* Written using on-line Java Platform 1.2 API Specification |
||||||
|
* and JCL book. |
||||||
|
* Believed complete and correct. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* InputStream that computes a checksum of the data being read using a |
||||||
|
* supplied Checksum object. |
||||||
|
* |
||||||
|
* @author Tom Tromey |
||||||
|
* @date May 17, 1999 |
||||||
|
* @see Checksum |
||||||
|
*/ |
||||||
|
public class CheckedInputStream extends FilterInputStream { |
||||||
|
/** |
||||||
|
* Creates a new CheckInputStream on top of the supplied OutputStream |
||||||
|
* using the supplied Checksum. |
||||||
|
*/ |
||||||
|
public CheckedInputStream(InputStream in, Checksum sum) { |
||||||
|
super(in); |
||||||
|
this.sum = sum; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Checksum object used. To get the data checksum computed so |
||||||
|
* far call <code>getChecksum.getValue()</code>. |
||||||
|
*/ |
||||||
|
public Checksum getChecksum() { |
||||||
|
return sum; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads one byte, updates the checksum and returns the read byte |
||||||
|
* (or -1 when the end of file was reached). |
||||||
|
*/ |
||||||
|
public int read() throws IOException { |
||||||
|
int x = in.read(); |
||||||
|
if (x != -1) |
||||||
|
sum.update(x); |
||||||
|
return x; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads at most len bytes in the supplied buffer and updates the checksum |
||||||
|
* with it. Returns the number of bytes actually read or -1 when the end |
||||||
|
* of file was reached. |
||||||
|
*/ |
||||||
|
public int read(byte[] buf, int off, int len) throws IOException { |
||||||
|
int r = in.read(buf, off, len); |
||||||
|
if (r != -1) |
||||||
|
sum.update(buf, off, r); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Skips n bytes by reading them in a temporary buffer and updating the |
||||||
|
* the checksum with that buffer. Returns the actual number of bytes skiped |
||||||
|
* which can be less then requested when the end of file is reached. |
||||||
|
*/ |
||||||
|
public long skip(long n) throws IOException { |
||||||
|
if (n == 0) |
||||||
|
return 0; |
||||||
|
|
||||||
|
int min = (int) Math.min(n, 1024); |
||||||
|
byte[] buf = new byte[min]; |
||||||
|
|
||||||
|
long s = 0; |
||||||
|
while (n > 0) { |
||||||
|
int r = in.read(buf, 0, min); |
||||||
|
if (r == -1) |
||||||
|
break; |
||||||
|
n -= r; |
||||||
|
s += r; |
||||||
|
min = (int) Math.min(n, 1024); |
||||||
|
sum.update(buf, 0, r); |
||||||
|
} |
||||||
|
|
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The checksum object. |
||||||
|
*/ |
||||||
|
private final Checksum sum; |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
/* CheckedOutputStream.java - Compute checksum of data being written. |
||||||
|
Copyright (C) 1999, 2000 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.FilterOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
|
||||||
|
/* Written using on-line Java Platform 1.2 API Specification |
||||||
|
* and JCL book. |
||||||
|
* Believed complete and correct. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* OutputStream that computes a checksum of data being written using a |
||||||
|
* supplied Checksum object. |
||||||
|
* |
||||||
|
* @author Tom Tromey |
||||||
|
* @date May 17, 1999 |
||||||
|
* @see Checksum |
||||||
|
*/ |
||||||
|
public class CheckedOutputStream extends FilterOutputStream { |
||||||
|
/** |
||||||
|
* Creates a new CheckInputStream on top of the supplied OutputStream |
||||||
|
* using the supplied Checksum. |
||||||
|
*/ |
||||||
|
public CheckedOutputStream(OutputStream out, Checksum cksum) { |
||||||
|
super(out); |
||||||
|
this.sum = cksum; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the Checksum object used. To get the data checksum computed so |
||||||
|
* far call <code>getChecksum.getValue()</code>. |
||||||
|
*/ |
||||||
|
public Checksum getChecksum() { |
||||||
|
return sum; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes one byte to the OutputStream and updates the Checksum. |
||||||
|
*/ |
||||||
|
public void write(int bval) throws IOException { |
||||||
|
out.write(bval); |
||||||
|
sum.update(bval); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes the byte array to the OutputStream and updates the Checksum. |
||||||
|
*/ |
||||||
|
public void write(byte[] buf, int off, int len) throws IOException { |
||||||
|
out.write(buf, off, len); |
||||||
|
sum.update(buf, off, len); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The checksum object. |
||||||
|
*/ |
||||||
|
private Checksum sum; |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
/* Checksum.java - Interface to compute a data checksum |
||||||
|
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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/* |
||||||
|
* Written using on-line Java Platform 1.2 API Specification, as well |
||||||
|
* as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). |
||||||
|
* Status: Believed complete and correct. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface to compute a data checksum used by checked input/output streams. |
||||||
|
* A data checksum can be updated by one byte or with a byte array. After each |
||||||
|
* update the value of the current checksum can be returned by calling |
||||||
|
* <code>getValue</code>. The complete checksum object can also be reset |
||||||
|
* so it can be used again with new data. |
||||||
|
* |
||||||
|
* @author Per Bothner |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @see CheckedInputStream |
||||||
|
* @see CheckedOutputStream |
||||||
|
*/ |
||||||
|
public interface Checksum { |
||||||
|
/** |
||||||
|
* Returns the data checksum computed so far. |
||||||
|
*/ |
||||||
|
long getValue(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Resets the data checksum as if no update was ever called. |
||||||
|
*/ |
||||||
|
void reset(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds one byte to the data checksum. |
||||||
|
* |
||||||
|
* @param bval the data value to add. The high byte of the int is ignored. |
||||||
|
*/ |
||||||
|
void update(int bval); |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
void update(byte[] buf, int off, int len); |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* DataformatException.java -- thrown when compressed data is corrupt |
||||||
|
Copyright (C) 1999, 2000, 2001, 2002 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception thrown when compressed data is corrupt. |
||||||
|
* |
||||||
|
* @author Tom Tromey |
||||||
|
* @author John Leuner |
||||||
|
* @status updated to 1.4 |
||||||
|
* @since 1.1 |
||||||
|
*/ |
||||||
|
public class DataFormatException extends Exception { |
||||||
|
/** |
||||||
|
* Compatible with JDK 1.1+. |
||||||
|
*/ |
||||||
|
private static final long serialVersionUID = 2219632870893641452L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an exception without a message. |
||||||
|
*/ |
||||||
|
public DataFormatException() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an exception with a message. |
||||||
|
* |
||||||
|
* @param msg the message |
||||||
|
*/ |
||||||
|
public DataFormatException(String msg) { |
||||||
|
super(msg); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,512 @@ |
|||||||
|
/* Deflater.java - Compress a data stream |
||||||
|
Copyright (C) 1999, 2000, 2001, 2004 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the Deflater class. The deflater class compresses input |
||||||
|
* with the deflate algorithm described in RFC 1951. It has several |
||||||
|
* compression levels and three different strategies described below. |
||||||
|
* <p> |
||||||
|
* This class is <i>not</i> thread safe. This is inherent in the API, due |
||||||
|
* to the split of deflate and setInput. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @author Tom Tromey |
||||||
|
*/ |
||||||
|
public class Deflater { |
||||||
|
/** |
||||||
|
* The best and slowest compression level. This tries to find very |
||||||
|
* long and distant string repetitions. |
||||||
|
*/ |
||||||
|
public static final int BEST_COMPRESSION = 9; |
||||||
|
/** |
||||||
|
* The worst but fastest compression level. |
||||||
|
*/ |
||||||
|
public static final int BEST_SPEED = 1; |
||||||
|
/** |
||||||
|
* The default compression level. |
||||||
|
*/ |
||||||
|
public static final int DEFAULT_COMPRESSION = -1; |
||||||
|
/** |
||||||
|
* This level won't compress at all but output uncompressed blocks. |
||||||
|
*/ |
||||||
|
public static final int NO_COMPRESSION = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* The default strategy. |
||||||
|
*/ |
||||||
|
public static final int DEFAULT_STRATEGY = 0; |
||||||
|
/** |
||||||
|
* This strategy will only allow longer string repetitions. It is |
||||||
|
* useful for random data with a small character set. |
||||||
|
*/ |
||||||
|
public static final int FILTERED = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* This strategy will not look for string repetitions at all. It |
||||||
|
* only encodes with Huffman trees (which means, that more common |
||||||
|
* characters get a smaller encoding. |
||||||
|
*/ |
||||||
|
public static final int HUFFMAN_ONLY = 2; |
||||||
|
|
||||||
|
/** |
||||||
|
* The compression method. This is the only method supported so far. |
||||||
|
* There is no need to use this constant at all. |
||||||
|
*/ |
||||||
|
public static final int DEFLATED = 8; |
||||||
|
|
||||||
|
/* |
||||||
|
* The Deflater can do the following state transitions: |
||||||
|
* |
||||||
|
* (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. |
||||||
|
* / | (2) (5) | |
||||||
|
* / v (5) | |
||||||
|
* (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) |
||||||
|
* \ | (3) | ,-------' |
||||||
|
* | | | (3) / |
||||||
|
* v v (5) v v |
||||||
|
* (1) -> BUSY_STATE ----> FINISHING_STATE |
||||||
|
* | (6) |
||||||
|
* v |
||||||
|
* FINISHED_STATE |
||||||
|
* \_____________________________________/ |
||||||
|
* | (7) |
||||||
|
* v |
||||||
|
* CLOSED_STATE |
||||||
|
* |
||||||
|
* (1) If we should produce a header we start in INIT_STATE, otherwise |
||||||
|
* we start in BUSY_STATE. |
||||||
|
* (2) A dictionary may be set only when we are in INIT_STATE, then |
||||||
|
* we change the state as indicated. |
||||||
|
* (3) Whether a dictionary is set or not, on the first call of deflate |
||||||
|
* we change to BUSY_STATE. |
||||||
|
* (4) -- intentionally left blank -- :) |
||||||
|
* (5) FINISHING_STATE is entered, when flush() is called to indicate that |
||||||
|
* there is no more INPUT. There are also states indicating, that |
||||||
|
* the header wasn't written yet. |
||||||
|
* (6) FINISHED_STATE is entered, when everything has been flushed to the |
||||||
|
* internal pending output buffer. |
||||||
|
* (7) At any time (7) |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
private static final int IS_SETDICT = 0x01; |
||||||
|
private static final int IS_FLUSHING = 0x04; |
||||||
|
private static final int IS_FINISHING = 0x08; |
||||||
|
|
||||||
|
private static final int INIT_STATE = 0x00; |
||||||
|
private static final int SETDICT_STATE = 0x01; |
||||||
|
private static final int INIT_FINISHING_STATE = 0x08; |
||||||
|
private static final int SETDICT_FINISHING_STATE = 0x09; |
||||||
|
private static final int BUSY_STATE = 0x10; |
||||||
|
private static final int FLUSHING_STATE = 0x14; |
||||||
|
private static final int FINISHING_STATE = 0x1c; |
||||||
|
private static final int FINISHED_STATE = 0x1e; |
||||||
|
private static final int CLOSED_STATE = 0x7f; |
||||||
|
|
||||||
|
/** |
||||||
|
* Compression level. |
||||||
|
*/ |
||||||
|
private int level; |
||||||
|
|
||||||
|
/** |
||||||
|
* should we include a header. |
||||||
|
*/ |
||||||
|
private boolean noHeader; |
||||||
|
|
||||||
|
/** |
||||||
|
* The current state. |
||||||
|
*/ |
||||||
|
private int state; |
||||||
|
|
||||||
|
/** |
||||||
|
* The total bytes of output written. |
||||||
|
*/ |
||||||
|
private int totalOut; |
||||||
|
|
||||||
|
/** |
||||||
|
* The pending output. |
||||||
|
*/ |
||||||
|
private DeflaterPending pending; |
||||||
|
|
||||||
|
/** |
||||||
|
* The deflater engine. |
||||||
|
*/ |
||||||
|
private DeflaterEngine engine; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new deflater with default compression level. |
||||||
|
*/ |
||||||
|
public Deflater() { |
||||||
|
this(DEFAULT_COMPRESSION, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new deflater with given compression level. |
||||||
|
* |
||||||
|
* @param lvl the compression level, a value between NO_COMPRESSION |
||||||
|
* and BEST_COMPRESSION, or DEFAULT_COMPRESSION. |
||||||
|
* @throws IllegalArgumentException if lvl is out of range. |
||||||
|
*/ |
||||||
|
public Deflater(int lvl) { |
||||||
|
this(lvl, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new deflater with given compression level. |
||||||
|
* |
||||||
|
* @param lvl the compression level, a value between NO_COMPRESSION |
||||||
|
* and BEST_COMPRESSION. |
||||||
|
* @param nowrap true, iff we should suppress the deflate header at the |
||||||
|
* beginning and the adler checksum at the end of the output. This is |
||||||
|
* useful for the GZIP format. |
||||||
|
* @throws IllegalArgumentException if lvl is out of range. |
||||||
|
*/ |
||||||
|
public Deflater(int lvl, boolean nowrap) { |
||||||
|
if (lvl == DEFAULT_COMPRESSION) |
||||||
|
lvl = 6; |
||||||
|
else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
|
||||||
|
pending = new DeflaterPending(); |
||||||
|
engine = new DeflaterEngine(pending); |
||||||
|
this.noHeader = nowrap; |
||||||
|
setStrategy(DEFAULT_STRATEGY); |
||||||
|
setLevel(lvl); |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resets the deflater. The deflater acts afterwards as if it was |
||||||
|
* just created with the same compression level and strategy as it |
||||||
|
* had before. |
||||||
|
*/ |
||||||
|
public void reset() { |
||||||
|
state = (noHeader ? BUSY_STATE : INIT_STATE); |
||||||
|
totalOut = 0; |
||||||
|
pending.reset(); |
||||||
|
engine.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Frees all objects allocated by the compressor. There's no |
||||||
|
* reason to call this, since you can just rely on garbage |
||||||
|
* collection. Exists only for compatibility against Sun's JDK, |
||||||
|
* where the compressor allocates native memory. |
||||||
|
* If you call any method (even reset) afterwards the behaviour is |
||||||
|
* <i>undefined</i>. |
||||||
|
* |
||||||
|
* @deprecated Just clear all references to deflater instead. |
||||||
|
*/ |
||||||
|
public void end() { |
||||||
|
engine = null; |
||||||
|
pending = null; |
||||||
|
state = CLOSED_STATE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the current adler checksum of the data that was processed so |
||||||
|
* far. |
||||||
|
*/ |
||||||
|
public int getAdler() { |
||||||
|
return engine.getAdler(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the number of input bytes processed so far. |
||||||
|
*/ |
||||||
|
public int getTotalIn() { |
||||||
|
return engine.getTotalIn(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the number of output bytes so far. |
||||||
|
*/ |
||||||
|
public int getTotalOut() { |
||||||
|
return totalOut; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finalizes this object. |
||||||
|
*/ |
||||||
|
protected void finalize() { |
||||||
|
/* Exists solely for compatibility. We don't have any native state. */ |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Flushes the current input block. Further calls to deflate() will |
||||||
|
* produce enough output to inflate everything in the current input |
||||||
|
* block. This is not part of Sun's JDK so I have made it package
|
||||||
|
* private. It is used by DeflaterOutputStream to implement |
||||||
|
* flush(). |
||||||
|
*/ |
||||||
|
void flush() { |
||||||
|
state |= IS_FLUSHING; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finishes the deflater with the current input block. It is an error |
||||||
|
* to give more input after this method was called. This method must |
||||||
|
* be called to force all bytes to be flushed. |
||||||
|
*/ |
||||||
|
public void finish() { |
||||||
|
state |= IS_FLUSHING | IS_FINISHING; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true iff the stream was finished and no more output bytes |
||||||
|
* are available. |
||||||
|
*/ |
||||||
|
public boolean finished() { |
||||||
|
return state == FINISHED_STATE && pending.isFlushed(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true, if the input buffer is empty. |
||||||
|
* You should then call setInput(). <br> |
||||||
|
* |
||||||
|
* <em>NOTE</em>: This method can also return true when the stream |
||||||
|
* was finished. |
||||||
|
*/ |
||||||
|
public boolean needsInput() { |
||||||
|
return engine.needsInput(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the data which should be compressed next. This should be only |
||||||
|
* called when needsInput indicates that more input is needed. |
||||||
|
* If you call setInput when needsInput() returns false, the |
||||||
|
* previous input that is still pending will be thrown away. |
||||||
|
* The given byte array should not be changed, before needsInput() returns |
||||||
|
* true again. |
||||||
|
* This call is equivalent to <code>setInput(input, 0, input.length)</code>. |
||||||
|
* |
||||||
|
* @param input the buffer containing the input data. |
||||||
|
* @throws IllegalStateException if the buffer was finished() or ended(). |
||||||
|
*/ |
||||||
|
public void setInput(byte[] input) { |
||||||
|
setInput(input, 0, input.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the data which should be compressed next. This should be |
||||||
|
* only called when needsInput indicates that more input is needed. |
||||||
|
* The given byte array should not be changed, before needsInput() returns |
||||||
|
* true again. |
||||||
|
* |
||||||
|
* @param input the buffer containing the input data. |
||||||
|
* @param off the start of the data. |
||||||
|
* @param len the length of the data. |
||||||
|
* @throws IllegalStateException if the buffer was finished() or ended() |
||||||
|
* or if previous input is still pending. |
||||||
|
*/ |
||||||
|
public void setInput(byte[] input, int off, int len) { |
||||||
|
if ((state & IS_FINISHING) != 0) |
||||||
|
throw new IllegalStateException("finish()/end() already called"); |
||||||
|
engine.setInput(input, off, len); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the compression level. There is no guarantee of the exact |
||||||
|
* position of the change, but if you call this when needsInput is |
||||||
|
* true the change of compression level will occur somewhere near |
||||||
|
* before the end of the so far given input. |
||||||
|
* |
||||||
|
* @param lvl the new compression level. |
||||||
|
*/ |
||||||
|
public void setLevel(int lvl) { |
||||||
|
if (lvl == DEFAULT_COMPRESSION) |
||||||
|
lvl = 6; |
||||||
|
else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
|
||||||
|
|
||||||
|
if (level != lvl) { |
||||||
|
level = lvl; |
||||||
|
engine.setLevel(lvl); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the compression strategy. Strategy is one of |
||||||
|
* DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact |
||||||
|
* position where the strategy is changed, the same as for |
||||||
|
* setLevel() applies. |
||||||
|
* |
||||||
|
* @param stgy the new compression strategy. |
||||||
|
*/ |
||||||
|
public void setStrategy(int stgy) { |
||||||
|
if (stgy != DEFAULT_STRATEGY && stgy != FILTERED |
||||||
|
&& stgy != HUFFMAN_ONLY) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
engine.setStrategy(stgy); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Deflates the current input block to the given array. It returns |
||||||
|
* the number of bytes compressed, or 0 if either |
||||||
|
* needsInput() or finished() returns true or length is zero. |
||||||
|
* |
||||||
|
* @param output the buffer where to write the compressed data. |
||||||
|
*/ |
||||||
|
public int deflate(byte[] output) { |
||||||
|
return deflate(output, 0, output.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Deflates the current input block to the given array. It returns |
||||||
|
* the number of bytes compressed, or 0 if either |
||||||
|
* needsInput() or finished() returns true or length is zero. |
||||||
|
* |
||||||
|
* @param output the buffer where to write the compressed data. |
||||||
|
* @param offset the offset into the output array. |
||||||
|
* @param length the maximum number of bytes that may be written. |
||||||
|
* @throws IllegalStateException if end() was called. |
||||||
|
* @throws IndexOutOfBoundsException if offset and/or length |
||||||
|
* don't match the array length. |
||||||
|
*/ |
||||||
|
public int deflate(byte[] output, int offset, int length) { |
||||||
|
int origLength = length; |
||||||
|
|
||||||
|
if (state == CLOSED_STATE) |
||||||
|
throw new IllegalStateException("Deflater closed"); |
||||||
|
|
||||||
|
if (state < BUSY_STATE) { |
||||||
|
/* output header */ |
||||||
|
int header = (DEFLATED + |
||||||
|
((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; |
||||||
|
int level_flags = (level - 1) >> 1; |
||||||
|
if (level_flags < 0 || level_flags > 3) |
||||||
|
level_flags = 3; |
||||||
|
header |= level_flags << 6; |
||||||
|
if ((state & IS_SETDICT) != 0) |
||||||
|
/* Dictionary was set */ |
||||||
|
header |= DeflaterConstants.PRESET_DICT; |
||||||
|
header += 31 - (header % 31); |
||||||
|
|
||||||
|
pending.writeShortMSB(header); |
||||||
|
if ((state & IS_SETDICT) != 0) { |
||||||
|
int chksum = engine.getAdler(); |
||||||
|
engine.resetAdler(); |
||||||
|
pending.writeShortMSB(chksum >> 16); |
||||||
|
pending.writeShortMSB(chksum & 0xffff); |
||||||
|
} |
||||||
|
|
||||||
|
state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); |
||||||
|
} |
||||||
|
|
||||||
|
for (; ; ) { |
||||||
|
int count = pending.flush(output, offset, length); |
||||||
|
offset += count; |
||||||
|
totalOut += count; |
||||||
|
length -= count; |
||||||
|
if (length == 0 || state == FINISHED_STATE) |
||||||
|
break; |
||||||
|
|
||||||
|
if (!engine.deflate((state & IS_FLUSHING) != 0, |
||||||
|
(state & IS_FINISHING) != 0)) { |
||||||
|
if (state == BUSY_STATE) |
||||||
|
/* We need more input now */ |
||||||
|
return origLength - length; |
||||||
|
else if (state == FLUSHING_STATE) { |
||||||
|
if (level != NO_COMPRESSION) { |
||||||
|
/* We have to supply some lookahead. 8 bit lookahead |
||||||
|
* are needed by the zlib inflater, and we must fill |
||||||
|
* the next byte, so that all bits are flushed. |
||||||
|
*/ |
||||||
|
int neededbits = 8 + ((-pending.getBitCount()) & 7); |
||||||
|
while (neededbits > 0) { |
||||||
|
/* write a static tree block consisting solely of |
||||||
|
* an EOF: |
||||||
|
*/ |
||||||
|
pending.writeBits(2, 10); |
||||||
|
neededbits -= 10; |
||||||
|
} |
||||||
|
} |
||||||
|
state = BUSY_STATE; |
||||||
|
} else if (state == FINISHING_STATE) { |
||||||
|
pending.alignToByte(); |
||||||
|
/* We have completed the stream */ |
||||||
|
if (!noHeader) { |
||||||
|
int adler = engine.getAdler(); |
||||||
|
pending.writeShortMSB(adler >> 16); |
||||||
|
pending.writeShortMSB(adler & 0xffff); |
||||||
|
} |
||||||
|
state = FINISHED_STATE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return origLength - length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the dictionary which should be used in the deflate process. |
||||||
|
* This call is equivalent to <code>setDictionary(dict, 0, |
||||||
|
* dict.length)</code>. |
||||||
|
* |
||||||
|
* @param dict the dictionary. |
||||||
|
* @throws IllegalStateException if setInput () or deflate () |
||||||
|
* were already called or another dictionary was already set. |
||||||
|
*/ |
||||||
|
public void setDictionary(byte[] dict) { |
||||||
|
setDictionary(dict, 0, dict.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the dictionary which should be used in the deflate process. |
||||||
|
* The dictionary should be a byte array containing strings that are |
||||||
|
* likely to occur in the data which should be compressed. The |
||||||
|
* dictionary is not stored in the compressed output, only a |
||||||
|
* checksum. To decompress the output you need to supply the same |
||||||
|
* dictionary again. |
||||||
|
* |
||||||
|
* @param dict the dictionary. |
||||||
|
* @param offset an offset into the dictionary. |
||||||
|
* @param length the length of the dictionary. |
||||||
|
* @throws IllegalStateException if setInput () or deflate () were |
||||||
|
* already called or another dictionary was already set. |
||||||
|
*/ |
||||||
|
public void setDictionary(byte[] dict, int offset, int length) { |
||||||
|
if (state != INIT_STATE) |
||||||
|
throw new IllegalStateException(); |
||||||
|
|
||||||
|
state = SETDICT_STATE; |
||||||
|
engine.setDictionary(dict, offset, length); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
/* me.ag2s.epublib.zip.DeflaterConstants |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
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}; |
||||||
|
} |
@ -0,0 +1,653 @@ |
|||||||
|
/* me.ag2s.epublib.zip.DeflaterEngine |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
class DeflaterEngine implements DeflaterConstants { |
||||||
|
private final static int TOO_FAR = 4096; |
||||||
|
|
||||||
|
private int ins_h; |
||||||
|
|
||||||
|
/** |
||||||
|
* Hashtable, hashing three characters to an index for window, so |
||||||
|
* that window[index]..window[index+2] have this hash code. |
||||||
|
* Note that the array should really be unsigned short, so you need |
||||||
|
* to and the values with 0xffff. |
||||||
|
*/ |
||||||
|
private short[] head; |
||||||
|
|
||||||
|
/** |
||||||
|
* prev[index & WMASK] points to the previous index that has the |
||||||
|
* same hash code as the string starting at index. This way |
||||||
|
* entries with the same hash code are in a linked list. |
||||||
|
* Note that the array should really be unsigned short, so you need |
||||||
|
* to and the values with 0xffff. |
||||||
|
*/ |
||||||
|
private short[] prev; |
||||||
|
|
||||||
|
private int matchStart, matchLen; |
||||||
|
private boolean prevAvailable; |
||||||
|
private int blockStart; |
||||||
|
|
||||||
|
/** |
||||||
|
* strstart points to the current character in window. |
||||||
|
*/ |
||||||
|
private int strstart; |
||||||
|
|
||||||
|
/** |
||||||
|
* lookahead is the number of characters starting at strstart in |
||||||
|
* window that are valid. |
||||||
|
* So window[strstart] until window[strstart+lookahead-1] are valid |
||||||
|
* characters. |
||||||
|
*/ |
||||||
|
private int lookahead; |
||||||
|
|
||||||
|
/** |
||||||
|
* This array contains the part of the uncompressed stream that |
||||||
|
* is of relevance. The current character is indexed by strstart. |
||||||
|
*/ |
||||||
|
private byte[] window; |
||||||
|
|
||||||
|
private int strategy, max_chain, max_lazy, niceLength, goodLength; |
||||||
|
|
||||||
|
/** |
||||||
|
* The current compression function. |
||||||
|
*/ |
||||||
|
private int comprFunc; |
||||||
|
|
||||||
|
/** |
||||||
|
* The input data for compression. |
||||||
|
*/ |
||||||
|
private byte[] inputBuf; |
||||||
|
|
||||||
|
/** |
||||||
|
* The total bytes of input read. |
||||||
|
*/ |
||||||
|
private int totalIn; |
||||||
|
|
||||||
|
/** |
||||||
|
* The offset into inputBuf, where input data starts. |
||||||
|
*/ |
||||||
|
private int inputOff; |
||||||
|
|
||||||
|
/** |
||||||
|
* The end offset of the input data. |
||||||
|
*/ |
||||||
|
private int inputEnd; |
||||||
|
|
||||||
|
private DeflaterPending pending; |
||||||
|
private DeflaterHuffman huffman; |
||||||
|
|
||||||
|
/** |
||||||
|
* The adler checksum |
||||||
|
*/ |
||||||
|
private Adler32 adler; |
||||||
|
|
||||||
|
/* DEFLATE ALGORITHM: |
||||||
|
* |
||||||
|
* The uncompressed stream is inserted into the window array. When |
||||||
|
* the window array is full the first half is thrown away and the |
||||||
|
* second half is copied to the beginning. |
||||||
|
* |
||||||
|
* The head array is a hash table. Three characters build a hash value |
||||||
|
* and they the value points to the corresponding index in window of |
||||||
|
* the last string with this hash. The prev array implements a |
||||||
|
* linked list of matches with the same hash: prev[index & WMASK] points |
||||||
|
* to the previous index with the same hash. |
||||||
|
* |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
DeflaterEngine(DeflaterPending pending) { |
||||||
|
this.pending = pending; |
||||||
|
huffman = new DeflaterHuffman(pending); |
||||||
|
adler = new Adler32(); |
||||||
|
|
||||||
|
window = new byte[2 * WSIZE]; |
||||||
|
head = new short[HASH_SIZE]; |
||||||
|
prev = new short[WSIZE]; |
||||||
|
|
||||||
|
/* We start at index 1, to avoid a implementation deficiency, that |
||||||
|
* we cannot build a repeat pattern at index 0. |
||||||
|
*/ |
||||||
|
blockStart = strstart = 1; |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
huffman.reset(); |
||||||
|
adler.reset(); |
||||||
|
blockStart = strstart = 1; |
||||||
|
lookahead = 0; |
||||||
|
totalIn = 0; |
||||||
|
prevAvailable = false; |
||||||
|
matchLen = MIN_MATCH - 1; |
||||||
|
for (int i = 0; i < HASH_SIZE; i++) |
||||||
|
head[i] = 0; |
||||||
|
for (int i = 0; i < WSIZE; i++) |
||||||
|
prev[i] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
public final void resetAdler() { |
||||||
|
adler.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
public final int getAdler() { |
||||||
|
int chksum = (int) adler.getValue(); |
||||||
|
return chksum; |
||||||
|
} |
||||||
|
|
||||||
|
public final int getTotalIn() { |
||||||
|
return totalIn; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setStrategy(int strat) { |
||||||
|
strategy = strat; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLevel(int lvl) { |
||||||
|
goodLength = DeflaterConstants.GOOD_LENGTH[lvl]; |
||||||
|
max_lazy = DeflaterConstants.MAX_LAZY[lvl]; |
||||||
|
niceLength = DeflaterConstants.NICE_LENGTH[lvl]; |
||||||
|
max_chain = DeflaterConstants.MAX_CHAIN[lvl]; |
||||||
|
|
||||||
|
if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) { |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("Change from " + comprFunc + " to " |
||||||
|
+ DeflaterConstants.COMPR_FUNC[lvl]); |
||||||
|
switch (comprFunc) { |
||||||
|
case DEFLATE_STORED: |
||||||
|
if (strstart > blockStart) { |
||||||
|
huffman.flushStoredBlock(window, blockStart, |
||||||
|
strstart - blockStart, false); |
||||||
|
blockStart = strstart; |
||||||
|
} |
||||||
|
updateHash(); |
||||||
|
break; |
||||||
|
case DEFLATE_FAST: |
||||||
|
if (strstart > blockStart) { |
||||||
|
huffman.flushBlock(window, blockStart, strstart - blockStart, |
||||||
|
false); |
||||||
|
blockStart = strstart; |
||||||
|
} |
||||||
|
break; |
||||||
|
case DEFLATE_SLOW: |
||||||
|
if (prevAvailable) |
||||||
|
huffman.tallyLit(window[strstart - 1] & 0xff); |
||||||
|
if (strstart > blockStart) { |
||||||
|
huffman.flushBlock(window, blockStart, strstart - blockStart, |
||||||
|
false); |
||||||
|
blockStart = strstart; |
||||||
|
} |
||||||
|
prevAvailable = false; |
||||||
|
matchLen = MIN_MATCH - 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
comprFunc = COMPR_FUNC[lvl]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private final void updateHash() { |
||||||
|
if (DEBUGGING) |
||||||
|
System.err.println("updateHash: " + strstart); |
||||||
|
ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Inserts the current string in the head hash and returns the previous |
||||||
|
* value for this hash. |
||||||
|
*/ |
||||||
|
private final int insertString() { |
||||||
|
short match; |
||||||
|
int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH - 1)]) |
||||||
|
& HASH_MASK; |
||||||
|
|
||||||
|
if (DEBUGGING) { |
||||||
|
if (hash != (((window[strstart] << (2 * HASH_SHIFT)) |
||||||
|
^ (window[strstart + 1] << HASH_SHIFT) |
||||||
|
^ (window[strstart + 2])) & HASH_MASK)) |
||||||
|
throw new InternalError("hash inconsistent: " + hash + "/" |
||||||
|
+ window[strstart] + "," |
||||||
|
+ window[strstart + 1] + "," |
||||||
|
+ window[strstart + 2] + "," + HASH_SHIFT); |
||||||
|
} |
||||||
|
|
||||||
|
prev[strstart & WMASK] = match = head[hash]; |
||||||
|
head[hash] = (short) strstart; |
||||||
|
ins_h = hash; |
||||||
|
return match & 0xffff; |
||||||
|
} |
||||||
|
|
||||||
|
private void slideWindow() { |
||||||
|
System.arraycopy(window, WSIZE, window, 0, WSIZE); |
||||||
|
matchStart -= WSIZE; |
||||||
|
strstart -= WSIZE; |
||||||
|
blockStart -= WSIZE; |
||||||
|
|
||||||
|
/* Slide the hash table (could be avoided with 32 bit values |
||||||
|
* at the expense of memory usage). |
||||||
|
*/ |
||||||
|
for (int i = 0; i < HASH_SIZE; i++) { |
||||||
|
int m = head[i] & 0xffff; |
||||||
|
head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* Slide the prev table. |
||||||
|
*/ |
||||||
|
for (int i = 0; i < WSIZE; i++) { |
||||||
|
int m = prev[i] & 0xffff; |
||||||
|
prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Fill the window when the lookahead becomes insufficient. |
||||||
|
* Updates strstart and lookahead. |
||||||
|
* <p> |
||||||
|
* OUT assertions: strstart + lookahead <= 2*WSIZE |
||||||
|
* lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd |
||||||
|
*/ |
||||||
|
private void fillWindow() { |
||||||
|
/* If the window is almost full and there is insufficient lookahead, |
||||||
|
* move the upper half to the lower one to make room in the upper half. |
||||||
|
*/ |
||||||
|
if (strstart >= WSIZE + MAX_DIST) |
||||||
|
slideWindow(); |
||||||
|
|
||||||
|
/* If there is not enough lookahead, but still some input left, |
||||||
|
* read in the input |
||||||
|
*/ |
||||||
|
while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) { |
||||||
|
int more = 2 * WSIZE - lookahead - strstart; |
||||||
|
|
||||||
|
if (more > inputEnd - inputOff) |
||||||
|
more = inputEnd - inputOff; |
||||||
|
|
||||||
|
System.arraycopy(inputBuf, inputOff, |
||||||
|
window, strstart + lookahead, more); |
||||||
|
adler.update(inputBuf, inputOff, more); |
||||||
|
inputOff += more; |
||||||
|
totalIn += more; |
||||||
|
lookahead += more; |
||||||
|
} |
||||||
|
|
||||||
|
if (lookahead >= MIN_MATCH) |
||||||
|
updateHash(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Find the best (longest) string in the window matching the |
||||||
|
* string starting at strstart. |
||||||
|
* <p> |
||||||
|
* Preconditions: |
||||||
|
* strstart + MAX_MATCH <= window.length. |
||||||
|
* |
||||||
|
* @param curMatch |
||||||
|
*/ |
||||||
|
private boolean findLongestMatch(int curMatch) { |
||||||
|
int chainLength = this.max_chain; |
||||||
|
int niceLength = this.niceLength; |
||||||
|
short[] prev = this.prev; |
||||||
|
int scan = this.strstart; |
||||||
|
int match; |
||||||
|
int best_end = this.strstart + matchLen; |
||||||
|
int best_len = Math.max(matchLen, MIN_MATCH - 1); |
||||||
|
|
||||||
|
int limit = Math.max(strstart - MAX_DIST, 0); |
||||||
|
|
||||||
|
int strend = scan + MAX_MATCH - 1; |
||||||
|
byte scan_end1 = window[best_end - 1]; |
||||||
|
byte scan_end = window[best_end]; |
||||||
|
|
||||||
|
/* Do not waste too much time if we already have a good match: */ |
||||||
|
if (best_len >= this.goodLength) |
||||||
|
chainLength >>= 2; |
||||||
|
|
||||||
|
/* Do not look for matches beyond the end of the input. This is necessary |
||||||
|
* to make deflate deterministic. |
||||||
|
*/ |
||||||
|
if (niceLength > lookahead) |
||||||
|
niceLength = lookahead; |
||||||
|
|
||||||
|
if (DeflaterConstants.DEBUGGING |
||||||
|
&& strstart > 2 * WSIZE - MIN_LOOKAHEAD) |
||||||
|
throw new InternalError("need lookahead"); |
||||||
|
|
||||||
|
do { |
||||||
|
if (DeflaterConstants.DEBUGGING && curMatch >= strstart) |
||||||
|
throw new InternalError("future match"); |
||||||
|
if (window[curMatch + best_len] != scan_end |
||||||
|
|| window[curMatch + best_len - 1] != scan_end1 |
||||||
|
|| window[curMatch] != window[scan] |
||||||
|
|| window[curMatch + 1] != window[scan + 1]) |
||||||
|
continue; |
||||||
|
|
||||||
|
match = curMatch + 2; |
||||||
|
scan += 2; |
||||||
|
|
||||||
|
/* We check for insufficient lookahead only every 8th comparison; |
||||||
|
* the 256th check will be made at strstart+258. |
||||||
|
*/ |
||||||
|
while (window[++scan] == window[++match] |
||||||
|
&& window[++scan] == window[++match] |
||||||
|
&& window[++scan] == window[++match] |
||||||
|
&& window[++scan] == window[++match] |
||||||
|
&& window[++scan] == window[++match] |
||||||
|
&& window[++scan] == window[++match] |
||||||
|
&& window[++scan] == window[++match] |
||||||
|
&& window[++scan] == window[++match] |
||||||
|
&& scan < strend) ; |
||||||
|
|
||||||
|
if (scan > best_end) { |
||||||
|
// if (DeflaterConstants.DEBUGGING && ins_h == 0)
|
||||||
|
// System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
|
||||||
|
matchStart = curMatch; |
||||||
|
best_end = scan; |
||||||
|
best_len = scan - strstart; |
||||||
|
if (best_len >= niceLength) |
||||||
|
break; |
||||||
|
|
||||||
|
scan_end1 = window[best_end - 1]; |
||||||
|
scan_end = window[best_end]; |
||||||
|
} |
||||||
|
scan = strstart; |
||||||
|
} while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit |
||||||
|
&& --chainLength != 0); |
||||||
|
|
||||||
|
matchLen = Math.min(best_len, lookahead); |
||||||
|
return matchLen >= MIN_MATCH; |
||||||
|
} |
||||||
|
|
||||||
|
void setDictionary(byte[] buffer, int offset, int length) { |
||||||
|
if (DeflaterConstants.DEBUGGING && strstart != 1) |
||||||
|
throw new IllegalStateException("strstart not 1"); |
||||||
|
adler.update(buffer, offset, length); |
||||||
|
if (length < MIN_MATCH) |
||||||
|
return; |
||||||
|
if (length > MAX_DIST) { |
||||||
|
offset += length - MAX_DIST; |
||||||
|
length = MAX_DIST; |
||||||
|
} |
||||||
|
|
||||||
|
System.arraycopy(buffer, offset, window, strstart, length); |
||||||
|
|
||||||
|
updateHash(); |
||||||
|
length--; |
||||||
|
while (--length > 0) { |
||||||
|
insertString(); |
||||||
|
strstart++; |
||||||
|
} |
||||||
|
strstart += 2; |
||||||
|
blockStart = strstart; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean deflateStored(boolean flush, boolean finish) { |
||||||
|
if (!flush && lookahead == 0) |
||||||
|
return false; |
||||||
|
|
||||||
|
strstart += lookahead; |
||||||
|
lookahead = 0; |
||||||
|
|
||||||
|
int storedLen = strstart - blockStart; |
||||||
|
|
||||||
|
if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) |
||||||
|
/* Block is full */ |
||||||
|
|| (blockStart < WSIZE && storedLen >= MAX_DIST) |
||||||
|
/* Block may move out of window */ |
||||||
|
|| flush) { |
||||||
|
boolean lastBlock = finish; |
||||||
|
if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE) { |
||||||
|
storedLen = DeflaterConstants.MAX_BLOCK_SIZE; |
||||||
|
lastBlock = false; |
||||||
|
} |
||||||
|
|
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("storedBlock[" + storedLen + "," + lastBlock + "]"); |
||||||
|
|
||||||
|
huffman.flushStoredBlock(window, blockStart, storedLen, lastBlock); |
||||||
|
blockStart += storedLen; |
||||||
|
return !lastBlock; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean deflateFast(boolean flush, boolean finish) { |
||||||
|
if (lookahead < MIN_LOOKAHEAD && !flush) |
||||||
|
return false; |
||||||
|
|
||||||
|
while (lookahead >= MIN_LOOKAHEAD || flush) { |
||||||
|
if (lookahead == 0) { |
||||||
|
/* We are flushing everything */ |
||||||
|
huffman.flushBlock(window, blockStart, strstart - blockStart, |
||||||
|
finish); |
||||||
|
blockStart = strstart; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (strstart > 2 * WSIZE - MIN_LOOKAHEAD) { |
||||||
|
/* slide window, as findLongestMatch need this. |
||||||
|
* This should only happen when flushing and the window |
||||||
|
* is almost full. |
||||||
|
*/ |
||||||
|
slideWindow(); |
||||||
|
} |
||||||
|
|
||||||
|
int hashHead; |
||||||
|
if (lookahead >= MIN_MATCH |
||||||
|
&& (hashHead = insertString()) != 0 |
||||||
|
&& strategy != Deflater.HUFFMAN_ONLY |
||||||
|
&& strstart - hashHead <= MAX_DIST |
||||||
|
&& findLongestMatch(hashHead)) { |
||||||
|
/* longestMatch sets matchStart and matchLen */ |
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
for (int i = 0; i < matchLen; i++) { |
||||||
|
if (window[strstart + i] != window[matchStart + i]) |
||||||
|
throw new InternalError(); |
||||||
|
} |
||||||
|
} |
||||||
|
huffman.tallyDist(strstart - matchStart, matchLen); |
||||||
|
|
||||||
|
lookahead -= matchLen; |
||||||
|
if (matchLen <= max_lazy && lookahead >= MIN_MATCH) { |
||||||
|
while (--matchLen > 0) { |
||||||
|
strstart++; |
||||||
|
insertString(); |
||||||
|
} |
||||||
|
strstart++; |
||||||
|
} else { |
||||||
|
strstart += matchLen; |
||||||
|
if (lookahead >= MIN_MATCH - 1) |
||||||
|
updateHash(); |
||||||
|
} |
||||||
|
matchLen = MIN_MATCH - 1; |
||||||
|
continue; |
||||||
|
} else { |
||||||
|
/* No match found */ |
||||||
|
huffman.tallyLit(window[strstart] & 0xff); |
||||||
|
strstart++; |
||||||
|
lookahead--; |
||||||
|
} |
||||||
|
|
||||||
|
if (huffman.isFull()) { |
||||||
|
boolean lastBlock = finish && lookahead == 0; |
||||||
|
huffman.flushBlock(window, blockStart, strstart - blockStart, |
||||||
|
lastBlock); |
||||||
|
blockStart = strstart; |
||||||
|
return !lastBlock; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean deflateSlow(boolean flush, boolean finish) { |
||||||
|
if (lookahead < MIN_LOOKAHEAD && !flush) |
||||||
|
return false; |
||||||
|
|
||||||
|
while (lookahead >= MIN_LOOKAHEAD || flush) { |
||||||
|
if (lookahead == 0) { |
||||||
|
if (prevAvailable) |
||||||
|
huffman.tallyLit(window[strstart - 1] & 0xff); |
||||||
|
prevAvailable = false; |
||||||
|
|
||||||
|
/* We are flushing everything */ |
||||||
|
if (DeflaterConstants.DEBUGGING && !flush) |
||||||
|
throw new InternalError("Not flushing, but no lookahead"); |
||||||
|
huffman.flushBlock(window, blockStart, strstart - blockStart, |
||||||
|
finish); |
||||||
|
blockStart = strstart; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD) { |
||||||
|
/* slide window, as findLongestMatch need this. |
||||||
|
* This should only happen when flushing and the window |
||||||
|
* is almost full. |
||||||
|
*/ |
||||||
|
slideWindow(); |
||||||
|
} |
||||||
|
|
||||||
|
int prevMatch = matchStart; |
||||||
|
int prevLen = matchLen; |
||||||
|
if (lookahead >= MIN_MATCH) { |
||||||
|
int hashHead = insertString(); |
||||||
|
if (strategy != Deflater.HUFFMAN_ONLY |
||||||
|
&& hashHead != 0 && strstart - hashHead <= MAX_DIST |
||||||
|
&& findLongestMatch(hashHead)) { |
||||||
|
/* longestMatch sets matchStart and matchLen */ |
||||||
|
|
||||||
|
/* Discard match if too small and too far away */ |
||||||
|
if (matchLen <= 5 |
||||||
|
&& (strategy == Deflater.FILTERED |
||||||
|
|| (matchLen == MIN_MATCH |
||||||
|
&& strstart - matchStart > TOO_FAR))) { |
||||||
|
matchLen = MIN_MATCH - 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* previous match was better */ |
||||||
|
if (prevLen >= MIN_MATCH && matchLen <= prevLen) { |
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
for (int i = 0; i < matchLen; i++) { |
||||||
|
if (window[strstart - 1 + i] != window[prevMatch + i]) |
||||||
|
throw new InternalError(); |
||||||
|
} |
||||||
|
} |
||||||
|
huffman.tallyDist(strstart - 1 - prevMatch, prevLen); |
||||||
|
prevLen -= 2; |
||||||
|
do { |
||||||
|
strstart++; |
||||||
|
lookahead--; |
||||||
|
if (lookahead >= MIN_MATCH) |
||||||
|
insertString(); |
||||||
|
} |
||||||
|
while (--prevLen > 0); |
||||||
|
strstart++; |
||||||
|
lookahead--; |
||||||
|
prevAvailable = false; |
||||||
|
matchLen = MIN_MATCH - 1; |
||||||
|
} else { |
||||||
|
if (prevAvailable) |
||||||
|
huffman.tallyLit(window[strstart - 1] & 0xff); |
||||||
|
prevAvailable = true; |
||||||
|
strstart++; |
||||||
|
lookahead--; |
||||||
|
} |
||||||
|
|
||||||
|
if (huffman.isFull()) { |
||||||
|
int len = strstart - blockStart; |
||||||
|
if (prevAvailable) |
||||||
|
len--; |
||||||
|
boolean lastBlock = (finish && lookahead == 0 && !prevAvailable); |
||||||
|
huffman.flushBlock(window, blockStart, len, lastBlock); |
||||||
|
blockStart += len; |
||||||
|
return !lastBlock; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean deflate(boolean flush, boolean finish) { |
||||||
|
boolean progress; |
||||||
|
do { |
||||||
|
fillWindow(); |
||||||
|
boolean canFlush = flush && inputOff == inputEnd; |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("window: [" + blockStart + "," + strstart + "," |
||||||
|
+ lookahead + "], " + comprFunc + "," + canFlush); |
||||||
|
switch (comprFunc) { |
||||||
|
case DEFLATE_STORED: |
||||||
|
progress = deflateStored(canFlush, finish); |
||||||
|
break; |
||||||
|
case DEFLATE_FAST: |
||||||
|
progress = deflateFast(canFlush, finish); |
||||||
|
break; |
||||||
|
case DEFLATE_SLOW: |
||||||
|
progress = deflateSlow(canFlush, finish); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new InternalError(); |
||||||
|
} |
||||||
|
} |
||||||
|
while (pending.isFlushed() /* repeat while we have no pending output */ |
||||||
|
&& progress); /* and progress was made */ |
||||||
|
|
||||||
|
return progress; |
||||||
|
} |
||||||
|
|
||||||
|
public void setInput(byte[] buf, int off, int len) { |
||||||
|
if (inputOff < inputEnd) |
||||||
|
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(); |
||||||
|
|
||||||
|
inputBuf = buf; |
||||||
|
inputOff = off; |
||||||
|
inputEnd = end; |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean needsInput() { |
||||||
|
return inputEnd == inputOff; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,704 @@ |
|||||||
|
/* me.ag2s.epublib.zip.DeflaterHuffman |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the DeflaterHuffman class. |
||||||
|
* <p> |
||||||
|
* This class is <i>not</i> thread safe. This is inherent in the API, due |
||||||
|
* to the split of deflate and setInput. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @date Jan 6, 2000 |
||||||
|
*/ |
||||||
|
class DeflaterHuffman { |
||||||
|
private static final int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); |
||||||
|
private static final int LITERAL_NUM = 286; |
||||||
|
private static final int DIST_NUM = 30; |
||||||
|
private static final int BITLEN_NUM = 19; |
||||||
|
private static final int REP_3_6 = 16; |
||||||
|
private static final int REP_3_10 = 17; |
||||||
|
private static final int REP_11_138 = 18; |
||||||
|
private static final int EOF_SYMBOL = 256; |
||||||
|
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}; |
||||||
|
|
||||||
|
private final static String bit4Reverse = |
||||||
|
"\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017"; |
||||||
|
|
||||||
|
class Tree { |
||||||
|
short[] freqs; |
||||||
|
short[] codes; |
||||||
|
byte[] length; |
||||||
|
int[] bl_counts; |
||||||
|
int minNumCodes, numCodes; |
||||||
|
int maxLength; |
||||||
|
|
||||||
|
Tree(int elems, int minCodes, int maxLength) { |
||||||
|
this.minNumCodes = minCodes; |
||||||
|
this.maxLength = maxLength; |
||||||
|
freqs = new short[elems]; |
||||||
|
bl_counts = new int[maxLength]; |
||||||
|
} |
||||||
|
|
||||||
|
void reset() { |
||||||
|
for (int i = 0; i < freqs.length; i++) |
||||||
|
freqs[i] = 0; |
||||||
|
codes = null; |
||||||
|
length = null; |
||||||
|
} |
||||||
|
|
||||||
|
final void writeSymbol(int code) { |
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
freqs[code]--; |
||||||
|
// System.err.print("writeSymbol("+freqs.length+","+code+"): ");
|
||||||
|
} |
||||||
|
pending.writeBits(codes[code] & 0xffff, length[code]); |
||||||
|
} |
||||||
|
|
||||||
|
final void checkEmpty() { |
||||||
|
boolean empty = true; |
||||||
|
for (int i = 0; i < freqs.length; i++) |
||||||
|
if (freqs[i] != 0) { |
||||||
|
System.err.println("freqs[" + i + "] == " + freqs[i]); |
||||||
|
empty = false; |
||||||
|
} |
||||||
|
if (!empty) |
||||||
|
throw new InternalError(); |
||||||
|
System.err.println("checkEmpty suceeded!"); |
||||||
|
} |
||||||
|
|
||||||
|
void setStaticCodes(short[] stCodes, byte[] stLength) { |
||||||
|
codes = stCodes; |
||||||
|
length = stLength; |
||||||
|
} |
||||||
|
|
||||||
|
public void buildCodes() { |
||||||
|
int[] nextCode = new int[maxLength]; |
||||||
|
int code = 0; |
||||||
|
codes = new short[freqs.length]; |
||||||
|
|
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("buildCodes: " + freqs.length); |
||||||
|
for (int bits = 0; bits < maxLength; bits++) { |
||||||
|
nextCode[bits] = code; |
||||||
|
code += bl_counts[bits] << (15 - bits); |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("bits: " + (bits + 1) + " count: " + bl_counts[bits] |
||||||
|
+ " nextCode: " + Integer.toHexString(code)); |
||||||
|
} |
||||||
|
if (DeflaterConstants.DEBUGGING && code != 65536) |
||||||
|
throw new RuntimeException("Inconsistent bl_counts!"); |
||||||
|
|
||||||
|
for (int i = 0; i < numCodes; i++) { |
||||||
|
int bits = length[i]; |
||||||
|
if (bits > 0) { |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("codes[" + i + "] = rev(" |
||||||
|
+ Integer.toHexString(nextCode[bits - 1]) + ")," |
||||||
|
+ bits); |
||||||
|
codes[i] = bitReverse(nextCode[bits - 1]); |
||||||
|
nextCode[bits - 1] += 1 << (16 - bits); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void buildLength(int childs[]) { |
||||||
|
this.length = new byte[freqs.length]; |
||||||
|
int numNodes = childs.length / 2; |
||||||
|
int numLeafs = (numNodes + 1) / 2; |
||||||
|
int overflow = 0; |
||||||
|
|
||||||
|
for (int i = 0; i < maxLength; i++) |
||||||
|
bl_counts[i] = 0; |
||||||
|
|
||||||
|
/* First calculate optimal bit lengths */ |
||||||
|
int lengths[] = new int[numNodes]; |
||||||
|
lengths[numNodes - 1] = 0; |
||||||
|
for (int i = numNodes - 1; i >= 0; i--) { |
||||||
|
if (childs[2 * i + 1] != -1) { |
||||||
|
int bitLength = lengths[i] + 1; |
||||||
|
if (bitLength > maxLength) { |
||||||
|
bitLength = maxLength; |
||||||
|
overflow++; |
||||||
|
} |
||||||
|
lengths[childs[2 * i]] = lengths[childs[2 * i + 1]] = bitLength; |
||||||
|
} else { |
||||||
|
/* A leaf node */ |
||||||
|
int bitLength = lengths[i]; |
||||||
|
bl_counts[bitLength - 1]++; |
||||||
|
this.length[childs[2 * i]] = (byte) lengths[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
System.err.println("Tree " + freqs.length + " lengths:"); |
||||||
|
for (int i = 0; i < numLeafs; i++) |
||||||
|
System.err.println("Node " + childs[2 * i] + " freq: " + freqs[childs[2 * i]] |
||||||
|
+ " len: " + length[childs[2 * i]]); |
||||||
|
} |
||||||
|
|
||||||
|
if (overflow == 0) |
||||||
|
return; |
||||||
|
|
||||||
|
int incrBitLen = maxLength - 1; |
||||||
|
do { |
||||||
|
/* Find the first bit length which could increase: */ |
||||||
|
while (bl_counts[--incrBitLen] == 0) |
||||||
|
; |
||||||
|
|
||||||
|
/* Move this node one down and remove a corresponding |
||||||
|
* amount of overflow nodes. |
||||||
|
*/ |
||||||
|
do { |
||||||
|
bl_counts[incrBitLen]--; |
||||||
|
bl_counts[++incrBitLen]++; |
||||||
|
overflow -= 1 << (maxLength - 1 - incrBitLen); |
||||||
|
} |
||||||
|
while (overflow > 0 && incrBitLen < maxLength - 1); |
||||||
|
} |
||||||
|
while (overflow > 0); |
||||||
|
|
||||||
|
/* We may have overshot above. Move some nodes from maxLength to |
||||||
|
* maxLength-1 in that case. |
||||||
|
*/ |
||||||
|
bl_counts[maxLength - 1] += overflow; |
||||||
|
bl_counts[maxLength - 2] -= overflow; |
||||||
|
|
||||||
|
/* Now recompute all bit lengths, scanning in increasing |
||||||
|
* frequency. It is simpler to reconstruct all lengths instead of |
||||||
|
* fixing only the wrong ones. This idea is taken from 'ar' |
||||||
|
* written by Haruhiko Okumura. |
||||||
|
* |
||||||
|
* The nodes were inserted with decreasing frequency into the childs |
||||||
|
* array. |
||||||
|
*/ |
||||||
|
int nodePtr = 2 * numLeafs; |
||||||
|
for (int bits = maxLength; bits != 0; bits--) { |
||||||
|
int n = bl_counts[bits - 1]; |
||||||
|
while (n > 0) { |
||||||
|
int childPtr = 2 * childs[nodePtr++]; |
||||||
|
if (childs[childPtr + 1] == -1) { |
||||||
|
/* We found another leaf */ |
||||||
|
length[childs[childPtr]] = (byte) bits; |
||||||
|
n--; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
System.err.println("*** After overflow elimination. ***"); |
||||||
|
for (int i = 0; i < numLeafs; i++) |
||||||
|
System.err.println("Node " + childs[2 * i] + " freq: " + freqs[childs[2 * i]] |
||||||
|
+ " len: " + length[childs[2 * i]]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void buildTree() { |
||||||
|
int numSymbols = freqs.length; |
||||||
|
|
||||||
|
/* heap is a priority queue, sorted by frequency, least frequent |
||||||
|
* nodes first. The heap is a binary tree, with the property, that |
||||||
|
* the parent node is smaller than both child nodes. This assures |
||||||
|
* that the smallest node is the first parent. |
||||||
|
* |
||||||
|
* The binary tree is encoded in an array: 0 is root node and |
||||||
|
* the nodes 2*n+1, 2*n+2 are the child nodes of node n. |
||||||
|
*/ |
||||||
|
int[] heap = new int[numSymbols]; |
||||||
|
int heapLen = 0; |
||||||
|
int maxCode = 0; |
||||||
|
for (int n = 0; n < numSymbols; n++) { |
||||||
|
int freq = freqs[n]; |
||||||
|
if (freq != 0) { |
||||||
|
/* Insert n into heap */ |
||||||
|
int pos = heapLen++; |
||||||
|
int ppos; |
||||||
|
while (pos > 0 && |
||||||
|
freqs[heap[ppos = (pos - 1) / 2]] > freq) { |
||||||
|
heap[pos] = heap[ppos]; |
||||||
|
pos = ppos; |
||||||
|
} |
||||||
|
heap[pos] = n; |
||||||
|
maxCode = n; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* We could encode a single literal with 0 bits but then we |
||||||
|
* don't see the literals. Therefore we force at least two |
||||||
|
* literals to avoid this case. We don't care about order in |
||||||
|
* this case, both literals get a 1 bit code. |
||||||
|
*/ |
||||||
|
while (heapLen < 2) { |
||||||
|
int node = maxCode < 2 ? ++maxCode : 0; |
||||||
|
heap[heapLen++] = node; |
||||||
|
} |
||||||
|
|
||||||
|
numCodes = Math.max(maxCode + 1, minNumCodes); |
||||||
|
|
||||||
|
int numLeafs = heapLen; |
||||||
|
int[] childs = new int[4 * heapLen - 2]; |
||||||
|
int[] values = new int[2 * heapLen - 1]; |
||||||
|
int numNodes = numLeafs; |
||||||
|
for (int i = 0; i < heapLen; i++) { |
||||||
|
int node = heap[i]; |
||||||
|
childs[2 * i] = node; |
||||||
|
childs[2 * i + 1] = -1; |
||||||
|
values[i] = freqs[node] << 8; |
||||||
|
heap[i] = i; |
||||||
|
} |
||||||
|
|
||||||
|
/* Construct the Huffman tree by repeatedly combining the least two |
||||||
|
* frequent nodes. |
||||||
|
*/ |
||||||
|
do { |
||||||
|
int first = heap[0]; |
||||||
|
int last = heap[--heapLen]; |
||||||
|
|
||||||
|
/* Propagate the hole to the leafs of the heap */ |
||||||
|
int ppos = 0; |
||||||
|
int path = 1; |
||||||
|
while (path < heapLen) { |
||||||
|
if (path + 1 < heapLen |
||||||
|
&& values[heap[path]] > values[heap[path + 1]]) |
||||||
|
path++; |
||||||
|
|
||||||
|
heap[ppos] = heap[path]; |
||||||
|
ppos = path; |
||||||
|
path = path * 2 + 1; |
||||||
|
} |
||||||
|
|
||||||
|
/* Now propagate the last element down along path. Normally |
||||||
|
* it shouldn't go too deep. |
||||||
|
*/ |
||||||
|
int lastVal = values[last]; |
||||||
|
while ((path = ppos) > 0 |
||||||
|
&& values[heap[ppos = (path - 1) / 2]] > lastVal) |
||||||
|
heap[path] = heap[ppos]; |
||||||
|
heap[path] = last; |
||||||
|
|
||||||
|
|
||||||
|
int second = heap[0]; |
||||||
|
|
||||||
|
/* Create a new node father of first and second */ |
||||||
|
last = numNodes++; |
||||||
|
childs[2 * last] = first; |
||||||
|
childs[2 * last + 1] = second; |
||||||
|
int mindepth = Math.min(values[first] & 0xff, values[second] & 0xff); |
||||||
|
values[last] = lastVal = values[first] + values[second] - mindepth + 1; |
||||||
|
|
||||||
|
/* Again, propagate the hole to the leafs */ |
||||||
|
ppos = 0; |
||||||
|
path = 1; |
||||||
|
while (path < heapLen) { |
||||||
|
if (path + 1 < heapLen |
||||||
|
&& values[heap[path]] > values[heap[path + 1]]) |
||||||
|
path++; |
||||||
|
|
||||||
|
heap[ppos] = heap[path]; |
||||||
|
ppos = path; |
||||||
|
path = ppos * 2 + 1; |
||||||
|
} |
||||||
|
|
||||||
|
/* Now propagate the new element down along path */ |
||||||
|
while ((path = ppos) > 0 |
||||||
|
&& values[heap[ppos = (path - 1) / 2]] > lastVal) |
||||||
|
heap[path] = heap[ppos]; |
||||||
|
heap[path] = last; |
||||||
|
} |
||||||
|
while (heapLen > 1); |
||||||
|
|
||||||
|
if (heap[0] != childs.length / 2 - 1) |
||||||
|
throw new RuntimeException("Weird!"); |
||||||
|
|
||||||
|
buildLength(childs); |
||||||
|
} |
||||||
|
|
||||||
|
int getEncodedLength() { |
||||||
|
int len = 0; |
||||||
|
for (int i = 0; i < freqs.length; i++) |
||||||
|
len += freqs[i] * length[i]; |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
void calcBLFreq(Tree blTree) { |
||||||
|
int max_count; /* max repeat count */ |
||||||
|
int min_count; /* min repeat count */ |
||||||
|
int count; /* repeat count of the current code */ |
||||||
|
int curlen = -1; /* length of current code */ |
||||||
|
|
||||||
|
int i = 0; |
||||||
|
while (i < numCodes) { |
||||||
|
count = 1; |
||||||
|
int nextlen = length[i]; |
||||||
|
if (nextlen == 0) { |
||||||
|
max_count = 138; |
||||||
|
min_count = 3; |
||||||
|
} else { |
||||||
|
max_count = 6; |
||||||
|
min_count = 3; |
||||||
|
if (curlen != nextlen) { |
||||||
|
blTree.freqs[nextlen]++; |
||||||
|
count = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
curlen = nextlen; |
||||||
|
i++; |
||||||
|
|
||||||
|
while (i < numCodes && curlen == length[i]) { |
||||||
|
i++; |
||||||
|
if (++count >= max_count) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (count < min_count) |
||||||
|
blTree.freqs[curlen] += count; |
||||||
|
else if (curlen != 0) |
||||||
|
blTree.freqs[REP_3_6]++; |
||||||
|
else if (count <= 10) |
||||||
|
blTree.freqs[REP_3_10]++; |
||||||
|
else |
||||||
|
blTree.freqs[REP_11_138]++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void writeTree(Tree blTree) { |
||||||
|
int max_count; /* max repeat count */ |
||||||
|
int min_count; /* min repeat count */ |
||||||
|
int count; /* repeat count of the current code */ |
||||||
|
int curlen = -1; /* length of current code */ |
||||||
|
|
||||||
|
int i = 0; |
||||||
|
while (i < numCodes) { |
||||||
|
count = 1; |
||||||
|
int nextlen = length[i]; |
||||||
|
if (nextlen == 0) { |
||||||
|
max_count = 138; |
||||||
|
min_count = 3; |
||||||
|
} else { |
||||||
|
max_count = 6; |
||||||
|
min_count = 3; |
||||||
|
if (curlen != nextlen) { |
||||||
|
blTree.writeSymbol(nextlen); |
||||||
|
count = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
curlen = nextlen; |
||||||
|
i++; |
||||||
|
|
||||||
|
while (i < numCodes && curlen == length[i]) { |
||||||
|
i++; |
||||||
|
if (++count >= max_count) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (count < min_count) { |
||||||
|
while (count-- > 0) |
||||||
|
blTree.writeSymbol(curlen); |
||||||
|
} else if (curlen != 0) { |
||||||
|
blTree.writeSymbol(REP_3_6); |
||||||
|
pending.writeBits(count - 3, 2); |
||||||
|
} else if (count <= 10) { |
||||||
|
blTree.writeSymbol(REP_3_10); |
||||||
|
pending.writeBits(count - 3, 3); |
||||||
|
} else { |
||||||
|
blTree.writeSymbol(REP_11_138); |
||||||
|
pending.writeBits(count - 11, 7); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
DeflaterPending pending; |
||||||
|
private Tree literalTree, distTree, blTree; |
||||||
|
|
||||||
|
private short d_buf[]; |
||||||
|
private byte l_buf[]; |
||||||
|
private int last_lit; |
||||||
|
private int extra_bits; |
||||||
|
|
||||||
|
private static short staticLCodes[]; |
||||||
|
private static byte staticLLength[]; |
||||||
|
private static short staticDCodes[]; |
||||||
|
private static byte staticDLength[]; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reverse the bits of a 16 bit value. |
||||||
|
*/ |
||||||
|
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)); |
||||||
|
} |
||||||
|
|
||||||
|
static { |
||||||
|
/* See RFC 1951 3.2.6 */ |
||||||
|
/* Literal codes */ |
||||||
|
staticLCodes = new short[LITERAL_NUM]; |
||||||
|
staticLLength = new byte[LITERAL_NUM]; |
||||||
|
int i = 0; |
||||||
|
while (i < 144) { |
||||||
|
staticLCodes[i] = bitReverse((0x030 + i) << 8); |
||||||
|
staticLLength[i++] = 8; |
||||||
|
} |
||||||
|
while (i < 256) { |
||||||
|
staticLCodes[i] = bitReverse((0x190 - 144 + i) << 7); |
||||||
|
staticLLength[i++] = 9; |
||||||
|
} |
||||||
|
while (i < 280) { |
||||||
|
staticLCodes[i] = bitReverse((0x000 - 256 + i) << 9); |
||||||
|
staticLLength[i++] = 7; |
||||||
|
} |
||||||
|
while (i < LITERAL_NUM) { |
||||||
|
staticLCodes[i] = bitReverse((0x0c0 - 280 + i) << 8); |
||||||
|
staticLLength[i++] = 8; |
||||||
|
} |
||||||
|
|
||||||
|
/* Distant codes */ |
||||||
|
staticDCodes = new short[DIST_NUM]; |
||||||
|
staticDLength = new byte[DIST_NUM]; |
||||||
|
for (i = 0; i < DIST_NUM; i++) { |
||||||
|
staticDCodes[i] = bitReverse(i << 11); |
||||||
|
staticDLength[i] = 5; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public DeflaterHuffman(DeflaterPending pending) { |
||||||
|
this.pending = pending; |
||||||
|
|
||||||
|
literalTree = new Tree(LITERAL_NUM, 257, 15); |
||||||
|
distTree = new Tree(DIST_NUM, 1, 15); |
||||||
|
blTree = new Tree(BITLEN_NUM, 4, 7); |
||||||
|
|
||||||
|
d_buf = new short[BUFSIZE]; |
||||||
|
l_buf = new byte[BUFSIZE]; |
||||||
|
} |
||||||
|
|
||||||
|
public final void reset() { |
||||||
|
last_lit = 0; |
||||||
|
extra_bits = 0; |
||||||
|
literalTree.reset(); |
||||||
|
distTree.reset(); |
||||||
|
blTree.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
private final int l_code(int len) { |
||||||
|
if (len == 255) |
||||||
|
return 285; |
||||||
|
|
||||||
|
int code = 257; |
||||||
|
while (len >= 8) { |
||||||
|
code += 4; |
||||||
|
len >>= 1; |
||||||
|
} |
||||||
|
return code + len; |
||||||
|
} |
||||||
|
|
||||||
|
private final int d_code(int distance) { |
||||||
|
int code = 0; |
||||||
|
while (distance >= 4) { |
||||||
|
code += 2; |
||||||
|
distance >>= 1; |
||||||
|
} |
||||||
|
return code + distance; |
||||||
|
} |
||||||
|
|
||||||
|
public void sendAllTrees(int blTreeCodes) { |
||||||
|
blTree.buildCodes(); |
||||||
|
literalTree.buildCodes(); |
||||||
|
distTree.buildCodes(); |
||||||
|
pending.writeBits(literalTree.numCodes - 257, 5); |
||||||
|
pending.writeBits(distTree.numCodes - 1, 5); |
||||||
|
pending.writeBits(blTreeCodes - 4, 4); |
||||||
|
for (int rank = 0; rank < blTreeCodes; rank++) |
||||||
|
pending.writeBits(blTree.length[BL_ORDER[rank]], 3); |
||||||
|
literalTree.writeTree(blTree); |
||||||
|
distTree.writeTree(blTree); |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
blTree.checkEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
public void compressBlock() { |
||||||
|
for (int i = 0; i < last_lit; i++) { |
||||||
|
int litlen = l_buf[i] & 0xff; |
||||||
|
int dist = d_buf[i]; |
||||||
|
if (dist-- != 0) { |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.print("[" + (dist + 1) + "," + (litlen + 3) + "]: "); |
||||||
|
|
||||||
|
int lc = l_code(litlen); |
||||||
|
literalTree.writeSymbol(lc); |
||||||
|
|
||||||
|
int bits = (lc - 261) / 4; |
||||||
|
if (bits > 0 && bits <= 5) |
||||||
|
pending.writeBits(litlen & ((1 << bits) - 1), bits); |
||||||
|
|
||||||
|
int dc = d_code(dist); |
||||||
|
distTree.writeSymbol(dc); |
||||||
|
|
||||||
|
bits = dc / 2 - 1; |
||||||
|
if (bits > 0) |
||||||
|
pending.writeBits(dist & ((1 << bits) - 1), bits); |
||||||
|
} else { |
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
if (litlen > 32 && litlen < 127) |
||||||
|
System.err.print("(" + (char) litlen + "): "); |
||||||
|
else |
||||||
|
System.err.print("{" + litlen + "}: "); |
||||||
|
} |
||||||
|
literalTree.writeSymbol(litlen); |
||||||
|
} |
||||||
|
} |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.print("EOF: "); |
||||||
|
literalTree.writeSymbol(EOF_SYMBOL); |
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
literalTree.checkEmpty(); |
||||||
|
distTree.checkEmpty(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void flushStoredBlock(byte[] stored, |
||||||
|
int stored_offset, int stored_len, |
||||||
|
boolean lastBlock) { |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("Flushing stored block " + stored_len); |
||||||
|
pending.writeBits((DeflaterConstants.STORED_BLOCK << 1) |
||||||
|
+ (lastBlock ? 1 : 0), 3); |
||||||
|
pending.alignToByte(); |
||||||
|
pending.writeShort(stored_len); |
||||||
|
pending.writeShort(~stored_len); |
||||||
|
pending.writeBlock(stored, stored_offset, stored_len); |
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
public void flushBlock(byte[] stored, int stored_offset, int stored_len, |
||||||
|
boolean lastBlock) { |
||||||
|
literalTree.freqs[EOF_SYMBOL]++; |
||||||
|
|
||||||
|
/* Build trees */ |
||||||
|
literalTree.buildTree(); |
||||||
|
distTree.buildTree(); |
||||||
|
|
||||||
|
/* Calculate bitlen frequency */ |
||||||
|
literalTree.calcBLFreq(blTree); |
||||||
|
distTree.calcBLFreq(blTree); |
||||||
|
|
||||||
|
/* Build bitlen tree */ |
||||||
|
blTree.buildTree(); |
||||||
|
|
||||||
|
int blTreeCodes = 4; |
||||||
|
for (int i = 18; i > blTreeCodes; i--) { |
||||||
|
if (blTree.length[BL_ORDER[i]] > 0) |
||||||
|
blTreeCodes = i + 1; |
||||||
|
} |
||||||
|
int opt_len = 14 + blTreeCodes * 3 + blTree.getEncodedLength() |
||||||
|
+ literalTree.getEncodedLength() + distTree.getEncodedLength() |
||||||
|
+ extra_bits; |
||||||
|
|
||||||
|
int static_len = extra_bits; |
||||||
|
for (int i = 0; i < LITERAL_NUM; i++) |
||||||
|
static_len += literalTree.freqs[i] * staticLLength[i]; |
||||||
|
for (int i = 0; i < DIST_NUM; i++) |
||||||
|
static_len += distTree.freqs[i] * staticDLength[i]; |
||||||
|
if (opt_len >= static_len) { |
||||||
|
/* Force static trees */ |
||||||
|
opt_len = static_len; |
||||||
|
} |
||||||
|
|
||||||
|
if (stored_offset >= 0 && stored_len + 4 < opt_len >> 3) { |
||||||
|
/* Store Block */ |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("Storing, since " + stored_len + " < " + opt_len |
||||||
|
+ " <= " + static_len); |
||||||
|
flushStoredBlock(stored, stored_offset, stored_len, lastBlock); |
||||||
|
} else if (opt_len == static_len) { |
||||||
|
/* Encode with static tree */ |
||||||
|
pending.writeBits((DeflaterConstants.STATIC_TREES << 1) |
||||||
|
+ (lastBlock ? 1 : 0), 3); |
||||||
|
literalTree.setStaticCodes(staticLCodes, staticLLength); |
||||||
|
distTree.setStaticCodes(staticDCodes, staticDLength); |
||||||
|
compressBlock(); |
||||||
|
reset(); |
||||||
|
} else { |
||||||
|
/* Encode with dynamic tree */ |
||||||
|
pending.writeBits((DeflaterConstants.DYN_TREES << 1) |
||||||
|
+ (lastBlock ? 1 : 0), 3); |
||||||
|
sendAllTrees(blTreeCodes); |
||||||
|
compressBlock(); |
||||||
|
reset(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean isFull() { |
||||||
|
return last_lit == BUFSIZE; |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean tallyLit(int lit) { |
||||||
|
if (DeflaterConstants.DEBUGGING) { |
||||||
|
if (lit > 32 && lit < 127) |
||||||
|
System.err.println("(" + (char) lit + ")"); |
||||||
|
else |
||||||
|
System.err.println("{" + lit + "}"); |
||||||
|
} |
||||||
|
d_buf[last_lit] = 0; |
||||||
|
l_buf[last_lit++] = (byte) lit; |
||||||
|
literalTree.freqs[lit]++; |
||||||
|
return last_lit == BUFSIZE; |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean tallyDist(int dist, int len) { |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("[" + dist + "," + len + "]"); |
||||||
|
|
||||||
|
d_buf[last_lit] = (short) dist; |
||||||
|
l_buf[last_lit++] = (byte) (len - 3); |
||||||
|
|
||||||
|
int lc = l_code(len - 3); |
||||||
|
literalTree.freqs[lc]++; |
||||||
|
if (lc >= 265 && lc < 285) |
||||||
|
extra_bits += (lc - 261) / 4; |
||||||
|
|
||||||
|
int dc = d_code(dist - 1); |
||||||
|
distTree.freqs[dc]++; |
||||||
|
if (dc >= 4) |
||||||
|
extra_bits += dc / 2 - 1; |
||||||
|
return last_lit == BUFSIZE; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,190 @@ |
|||||||
|
/* DeflaterOutputStream.java - Output filter for compressing. |
||||||
|
Copyright (C) 1999, 2000, 2001, 2004 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.FilterOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
|
||||||
|
/* Written using on-line Java Platform 1.2 API Specification |
||||||
|
* and JCL book. |
||||||
|
* Believed complete and correct. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* This is a special FilterOutputStream deflating the bytes that are |
||||||
|
* written through it. It uses the Deflater for deflating. |
||||||
|
* <p> |
||||||
|
* A special thing to be noted is that flush() doesn't flush |
||||||
|
* everything in Sun's JDK, but it does so in jazzlib. This is because |
||||||
|
* Sun's Deflater doesn't have a way to flush() everything, without |
||||||
|
* finishing the stream. |
||||||
|
* |
||||||
|
* @author Tom Tromey, Jochen Hoenicke |
||||||
|
* @date Jan 11, 2001 |
||||||
|
*/ |
||||||
|
public class DeflaterOutputStream extends FilterOutputStream { |
||||||
|
/** |
||||||
|
* This buffer is used temporarily to retrieve the bytes from the |
||||||
|
* deflater and write them to the underlying output stream. |
||||||
|
*/ |
||||||
|
protected byte[] buf; |
||||||
|
|
||||||
|
/** |
||||||
|
* The deflater which is used to deflate the stream. |
||||||
|
*/ |
||||||
|
protected Deflater def; |
||||||
|
|
||||||
|
/** |
||||||
|
* Deflates everything in the def's input buffers. This will call |
||||||
|
* <code>def.deflate()</code> until all bytes from the input buffers |
||||||
|
* are processed. |
||||||
|
*/ |
||||||
|
protected void deflate() throws IOException { |
||||||
|
while (!def.needsInput()) { |
||||||
|
int len = def.deflate(buf, 0, buf.length); |
||||||
|
|
||||||
|
// System.err.println("DOS deflated " + len + " out of " + buf.length);
|
||||||
|
if (len <= 0) |
||||||
|
break; |
||||||
|
out.write(buf, 0, len); |
||||||
|
} |
||||||
|
|
||||||
|
if (!def.needsInput()) |
||||||
|
throw new InternalError("Can't deflate all input?"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new DeflaterOutputStream with a default Deflater and |
||||||
|
* default buffer size. |
||||||
|
* |
||||||
|
* @param out the output stream where deflated output should be written. |
||||||
|
*/ |
||||||
|
public DeflaterOutputStream(OutputStream out) { |
||||||
|
this(out, new Deflater(), 512); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new DeflaterOutputStream with the given Deflater and |
||||||
|
* default buffer size. |
||||||
|
* |
||||||
|
* @param out the output stream where deflated output should be written. |
||||||
|
* @param defl the underlying deflater. |
||||||
|
*/ |
||||||
|
public DeflaterOutputStream(OutputStream out, Deflater defl) { |
||||||
|
this(out, defl, 512); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new DeflaterOutputStream with the given Deflater and |
||||||
|
* buffer size. |
||||||
|
* |
||||||
|
* @param out the output stream where deflated output should be written. |
||||||
|
* @param defl the underlying deflater. |
||||||
|
* @param bufsize the buffer size. |
||||||
|
* @throws IllegalArgumentException if bufsize isn't positive. |
||||||
|
*/ |
||||||
|
public DeflaterOutputStream(OutputStream out, Deflater defl, int bufsize) { |
||||||
|
super(out); |
||||||
|
if (bufsize <= 0) |
||||||
|
throw new IllegalArgumentException("bufsize <= 0"); |
||||||
|
buf = new byte[bufsize]; |
||||||
|
def = defl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Flushes the stream by calling flush() on the deflater and then |
||||||
|
* on the underlying stream. This ensures that all bytes are |
||||||
|
* flushed. This function doesn't work in Sun's JDK, but only in |
||||||
|
* jazzlib. |
||||||
|
*/ |
||||||
|
public void flush() throws IOException { |
||||||
|
def.flush(); |
||||||
|
deflate(); |
||||||
|
out.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finishes the stream by calling finish() on the deflater. This |
||||||
|
* was the only way to ensure that all bytes are flushed in Sun's |
||||||
|
* JDK. |
||||||
|
*/ |
||||||
|
public void finish() throws IOException { |
||||||
|
def.finish(); |
||||||
|
while (!def.finished()) { |
||||||
|
int len = def.deflate(buf, 0, buf.length); |
||||||
|
if (len <= 0) |
||||||
|
break; |
||||||
|
out.write(buf, 0, len); |
||||||
|
} |
||||||
|
if (!def.finished()) |
||||||
|
throw new InternalError("Can't deflate all input?"); |
||||||
|
out.flush(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls finish () and closes the stream. |
||||||
|
*/ |
||||||
|
public void close() throws IOException { |
||||||
|
finish(); |
||||||
|
out.close(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a single byte to the compressed output stream. |
||||||
|
* |
||||||
|
* @param bval the byte value. |
||||||
|
*/ |
||||||
|
public void write(int bval) throws IOException { |
||||||
|
byte[] b = new byte[1]; |
||||||
|
b[0] = (byte) bval; |
||||||
|
write(b, 0, 1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a len bytes from an array to the compressed stream. |
||||||
|
* |
||||||
|
* @param buf the byte array. |
||||||
|
* @param off the offset into the byte array where to start. |
||||||
|
* @param len the number of bytes to write. |
||||||
|
*/ |
||||||
|
public void write(byte[] buf, int off, int len) throws IOException { |
||||||
|
def.setInput(buf, off, len); |
||||||
|
deflate(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
/* me.ag2s.epublib.zip.DeflaterPending |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class stores the pending output of the Deflater. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @date Jan 5, 2000 |
||||||
|
*/ |
||||||
|
|
||||||
|
class DeflaterPending extends PendingBuffer { |
||||||
|
public DeflaterPending() { |
||||||
|
super(DeflaterConstants.PENDING_BUF_SIZE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,338 @@ |
|||||||
|
/* GZIPInputStream.java - Input filter for reading gzip file |
||||||
|
Copyright (C) 1999, 2000, 2001, 2002, 2004 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.EOFException; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* This filter stream is used to decompress a "GZIP" format stream. |
||||||
|
* The "GZIP" format is described in RFC 1952. |
||||||
|
* |
||||||
|
* @author John Leuner |
||||||
|
* @author Tom Tromey |
||||||
|
* @since JDK 1.1 |
||||||
|
*/ |
||||||
|
public class GZIPInputStream |
||||||
|
extends InflaterInputStream { |
||||||
|
/** |
||||||
|
* The magic number found at the start of a GZIP stream. |
||||||
|
*/ |
||||||
|
public static final int GZIP_MAGIC = 0x1f8b; |
||||||
|
|
||||||
|
/** |
||||||
|
* The mask for bit 0 of the flag byte. |
||||||
|
*/ |
||||||
|
static final int FTEXT = 0x1; |
||||||
|
|
||||||
|
/** |
||||||
|
* The mask for bit 1 of the flag byte. |
||||||
|
*/ |
||||||
|
static final int FHCRC = 0x2; |
||||||
|
|
||||||
|
/** |
||||||
|
* The mask for bit 2 of the flag byte. |
||||||
|
*/ |
||||||
|
static final int FEXTRA = 0x4; |
||||||
|
|
||||||
|
/** |
||||||
|
* The mask for bit 3 of the flag byte. |
||||||
|
*/ |
||||||
|
static final int FNAME = 0x8; |
||||||
|
|
||||||
|
/** |
||||||
|
* The mask for bit 4 of the flag byte. |
||||||
|
*/ |
||||||
|
static final int FCOMMENT = 0x10; |
||||||
|
|
||||||
|
/** |
||||||
|
* The CRC-32 checksum value for uncompressed data. |
||||||
|
*/ |
||||||
|
protected CRC32 crc; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates whether or not the end of the stream has been reached. |
||||||
|
*/ |
||||||
|
protected boolean eos; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicates whether or not the GZIP header has been read in. |
||||||
|
*/ |
||||||
|
private boolean readGZIPHeader; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a GZIPInputStream with the default buffer size. |
||||||
|
* |
||||||
|
* @param in The stream to read compressed data from |
||||||
|
* (in GZIP format). |
||||||
|
* @throws IOException if an error occurs during an I/O operation. |
||||||
|
*/ |
||||||
|
public GZIPInputStream(InputStream in) |
||||||
|
throws IOException { |
||||||
|
this(in, 4096); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a GZIPInputStream with the specified buffer size. |
||||||
|
* |
||||||
|
* @param in The stream to read compressed data from |
||||||
|
* (in GZIP format). |
||||||
|
* @param size The size of the buffer to use. |
||||||
|
* @throws IOException if an error occurs during an I/O operation. |
||||||
|
* @throws IllegalArgumentException if <code>size</code> |
||||||
|
* is less than or equal to 0. |
||||||
|
*/ |
||||||
|
public GZIPInputStream(InputStream in, int size) |
||||||
|
throws IOException { |
||||||
|
super(in, new Inflater(true), size); |
||||||
|
crc = new CRC32(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the input stream. |
||||||
|
* |
||||||
|
* @throws IOException if an error occurs during an I/O operation. |
||||||
|
*/ |
||||||
|
public void close() |
||||||
|
throws IOException { |
||||||
|
// Nothing to do here.
|
||||||
|
super.close(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads in GZIP-compressed data and stores it in uncompressed form |
||||||
|
* into an array of bytes. The method will block until either |
||||||
|
* enough input data becomes available or the compressed stream |
||||||
|
* reaches its end. |
||||||
|
* |
||||||
|
* @param buf the buffer into which the uncompressed data will |
||||||
|
* be stored. |
||||||
|
* @param offset the offset indicating where in <code>buf</code> |
||||||
|
* the uncompressed data should be placed. |
||||||
|
* @param len the number of uncompressed bytes to be read. |
||||||
|
*/ |
||||||
|
public int read(byte[] buf, int offset, int len) throws IOException { |
||||||
|
// We first have to slurp in the GZIP header, then we feed all the
|
||||||
|
// rest of the data to the superclass.
|
||||||
|
//
|
||||||
|
// As we do that we continually update the CRC32. Once the data is
|
||||||
|
// finished, we check the CRC32.
|
||||||
|
//
|
||||||
|
// This means we don't need our own buffer, as everything is done
|
||||||
|
// in the superclass.
|
||||||
|
if (!readGZIPHeader) |
||||||
|
readHeader(); |
||||||
|
|
||||||
|
if (eos) |
||||||
|
return -1; |
||||||
|
|
||||||
|
// System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len);
|
||||||
|
|
||||||
|
/* We don't have to read the header, |
||||||
|
* so we just grab data from the superclass. |
||||||
|
*/ |
||||||
|
int numRead = super.read(buf, offset, len); |
||||||
|
if (numRead > 0) |
||||||
|
crc.update(buf, offset, numRead); |
||||||
|
|
||||||
|
if (inf.finished()) |
||||||
|
readFooter(); |
||||||
|
return numRead; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Reads in the GZIP header. |
||||||
|
*/ |
||||||
|
private void readHeader() throws IOException { |
||||||
|
/* 1. Check the two magic bytes */ |
||||||
|
CRC32 headCRC = new CRC32(); |
||||||
|
int magic = in.read(); |
||||||
|
if (magic < 0) { |
||||||
|
eos = true; |
||||||
|
return; |
||||||
|
} |
||||||
|
headCRC.update(magic); |
||||||
|
if (magic != (GZIP_MAGIC >> 8)) |
||||||
|
throw new IOException("Error in GZIP header, first byte doesn't match"); |
||||||
|
|
||||||
|
magic = in.read(); |
||||||
|
if (magic != (GZIP_MAGIC & 0xff)) |
||||||
|
throw new IOException("Error in GZIP header, second byte doesn't match"); |
||||||
|
headCRC.update(magic); |
||||||
|
|
||||||
|
/* 2. Check the compression type (must be 8) */ |
||||||
|
int CM = in.read(); |
||||||
|
if (CM != 8) |
||||||
|
throw new IOException("Error in GZIP header, data not in deflate format"); |
||||||
|
headCRC.update(CM); |
||||||
|
|
||||||
|
/* 3. Check the flags */ |
||||||
|
int flags = in.read(); |
||||||
|
if (flags < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
headCRC.update(flags); |
||||||
|
|
||||||
|
/* This flag byte is divided into individual bits as follows: |
||||||
|
|
||||||
|
bit 0 FTEXT |
||||||
|
bit 1 FHCRC |
||||||
|
bit 2 FEXTRA |
||||||
|
bit 3 FNAME |
||||||
|
bit 4 FCOMMENT |
||||||
|
bit 5 reserved |
||||||
|
bit 6 reserved |
||||||
|
bit 7 reserved |
||||||
|
*/ |
||||||
|
|
||||||
|
/* 3.1 Check the reserved bits are zero */ |
||||||
|
if ((flags & 0xd0) != 0) |
||||||
|
throw new IOException("Reserved flag bits in GZIP header != 0"); |
||||||
|
|
||||||
|
/* 4.-6. Skip the modification time, extra flags, and OS type */ |
||||||
|
for (int i = 0; i < 6; i++) { |
||||||
|
int readByte = in.read(); |
||||||
|
if (readByte < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
headCRC.update(readByte); |
||||||
|
} |
||||||
|
|
||||||
|
/* 7. Read extra field */ |
||||||
|
if ((flags & FEXTRA) != 0) { |
||||||
|
/* Skip subfield id */ |
||||||
|
for (int i = 0; i < 2; i++) { |
||||||
|
int readByte = in.read(); |
||||||
|
if (readByte < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
headCRC.update(readByte); |
||||||
|
} |
||||||
|
if (in.read() < 0 || in.read() < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
|
||||||
|
int len1, len2, extraLen; |
||||||
|
len1 = in.read(); |
||||||
|
len2 = in.read(); |
||||||
|
if ((len1 < 0) || (len2 < 0)) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
headCRC.update(len1); |
||||||
|
headCRC.update(len2); |
||||||
|
|
||||||
|
extraLen = (len1 << 8) | len2; |
||||||
|
for (int i = 0; i < extraLen; i++) { |
||||||
|
int readByte = in.read(); |
||||||
|
if (readByte < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
headCRC.update(readByte); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* 8. Read file name */ |
||||||
|
if ((flags & FNAME) != 0) { |
||||||
|
int readByte; |
||||||
|
while ((readByte = in.read()) > 0) |
||||||
|
headCRC.update(readByte); |
||||||
|
if (readByte < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP file name"); |
||||||
|
headCRC.update(readByte); |
||||||
|
} |
||||||
|
|
||||||
|
/* 9. Read comment */ |
||||||
|
if ((flags & FCOMMENT) != 0) { |
||||||
|
int readByte; |
||||||
|
while ((readByte = in.read()) > 0) |
||||||
|
headCRC.update(readByte); |
||||||
|
|
||||||
|
if (readByte < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP comment"); |
||||||
|
headCRC.update(readByte); |
||||||
|
} |
||||||
|
|
||||||
|
/* 10. Read header CRC */ |
||||||
|
if ((flags & FHCRC) != 0) { |
||||||
|
int tempByte; |
||||||
|
int crcval = in.read(); |
||||||
|
if (crcval < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
|
||||||
|
tempByte = in.read(); |
||||||
|
if (tempByte < 0) |
||||||
|
throw new EOFException("Early EOF in GZIP header"); |
||||||
|
|
||||||
|
crcval = (crcval << 8) | tempByte; |
||||||
|
if (crcval != ((int) headCRC.getValue() & 0xffff)) |
||||||
|
throw new IOException("Header CRC value mismatch"); |
||||||
|
} |
||||||
|
|
||||||
|
readGZIPHeader = true; |
||||||
|
//System.err.println("Read GZIP header");
|
||||||
|
} |
||||||
|
|
||||||
|
private void readFooter() throws IOException { |
||||||
|
byte[] footer = new byte[8]; |
||||||
|
int avail = inf.getRemaining(); |
||||||
|
if (avail > 8) |
||||||
|
avail = 8; |
||||||
|
System.arraycopy(buf, len - inf.getRemaining(), footer, 0, avail); |
||||||
|
int needed = 8 - avail; |
||||||
|
while (needed > 0) { |
||||||
|
int count = in.read(footer, 8 - needed, needed); |
||||||
|
if (count <= 0) |
||||||
|
throw new EOFException("Early EOF in GZIP footer"); |
||||||
|
needed -= count; //Jewel Jan 16
|
||||||
|
} |
||||||
|
|
||||||
|
int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) |
||||||
|
| ((footer[2] & 0xff) << 16) | (footer[3] << 24); |
||||||
|
if (crcval != (int) crc.getValue()) |
||||||
|
throw new IOException("GZIP crc sum mismatch, theirs \"" |
||||||
|
+ Integer.toHexString(crcval) |
||||||
|
+ "\" and ours \"" |
||||||
|
+ Integer.toHexString((int) crc.getValue())); |
||||||
|
|
||||||
|
int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8) |
||||||
|
| ((footer[6] & 0xff) << 16) | (footer[7] << 24); |
||||||
|
if (total != inf.getTotalOut()) |
||||||
|
throw new IOException("Number of bytes mismatch"); |
||||||
|
|
||||||
|
/* FIXME" XXX Should we support multiple members. |
||||||
|
* Difficult, since there may be some bytes still in buf |
||||||
|
*/ |
||||||
|
eos = true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,146 @@ |
|||||||
|
/* GZIPOutputStream.java - Create a file in gzip format |
||||||
|
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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* This filter stream is used to compress a stream into a "GZIP" stream. |
||||||
|
* The "GZIP" format is described in RFC 1952. |
||||||
|
* |
||||||
|
* @author John Leuner |
||||||
|
* @author Tom Tromey |
||||||
|
* @since JDK 1.1 |
||||||
|
*/ |
||||||
|
|
||||||
|
/* Written using on-line Java Platform 1.2 API Specification |
||||||
|
* and JCL book. |
||||||
|
* Believed complete and correct. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class GZIPOutputStream extends DeflaterOutputStream { |
||||||
|
/** |
||||||
|
* CRC-32 value for uncompressed data |
||||||
|
*/ |
||||||
|
protected CRC32 crc; |
||||||
|
|
||||||
|
/* Creates a GZIPOutputStream with the default buffer size |
||||||
|
* |
||||||
|
* |
||||||
|
* @param out The stream to read data (to be compressed) from |
||||||
|
* |
||||||
|
*/ |
||||||
|
public GZIPOutputStream(OutputStream out) throws IOException { |
||||||
|
this(out, 4096); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a GZIPOutputStream with the specified buffer size |
||||||
|
* |
||||||
|
* @param out The stream to read compressed data from |
||||||
|
* @param size Size of the buffer to use |
||||||
|
*/ |
||||||
|
public GZIPOutputStream(OutputStream out, int size) throws IOException { |
||||||
|
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size); |
||||||
|
|
||||||
|
crc = new CRC32(); |
||||||
|
int mod_time = (int) (System.currentTimeMillis() / 1000L); |
||||||
|
byte[] gzipHeader = |
||||||
|
{ |
||||||
|
/* The two magic bytes */ |
||||||
|
(byte) (GZIPInputStream.GZIP_MAGIC >> 8), |
||||||
|
(byte) GZIPInputStream.GZIP_MAGIC, |
||||||
|
|
||||||
|
/* The compression type */ |
||||||
|
(byte) Deflater.DEFLATED, |
||||||
|
|
||||||
|
/* The flags (not set) */ |
||||||
|
0, |
||||||
|
|
||||||
|
/* The modification time */ |
||||||
|
(byte) mod_time, (byte) (mod_time >> 8), |
||||||
|
(byte) (mod_time >> 16), (byte) (mod_time >> 24), |
||||||
|
|
||||||
|
/* The extra flags */ |
||||||
|
0, |
||||||
|
|
||||||
|
/* The OS type (unknown) */ |
||||||
|
(byte) 255 |
||||||
|
}; |
||||||
|
|
||||||
|
out.write(gzipHeader); |
||||||
|
// System.err.println("wrote GZIP header (" + gzipHeader.length + " bytes )");
|
||||||
|
} |
||||||
|
|
||||||
|
public synchronized void write(byte[] buf, int off, int len) |
||||||
|
throws IOException { |
||||||
|
super.write(buf, off, len); |
||||||
|
crc.update(buf, off, len); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes remaining compressed output data to the output stream |
||||||
|
* and closes it. |
||||||
|
*/ |
||||||
|
public void close() throws IOException { |
||||||
|
finish(); |
||||||
|
out.close(); |
||||||
|
} |
||||||
|
|
||||||
|
public void finish() throws IOException { |
||||||
|
super.finish(); |
||||||
|
|
||||||
|
int totalin = def.getTotalIn(); |
||||||
|
int crcval = (int) (crc.getValue() & 0xffffffff); |
||||||
|
|
||||||
|
// System.err.println("CRC val is " + Integer.toHexString( crcval ) + " and length " + Integer.toHexString(totalin));
|
||||||
|
|
||||||
|
byte[] gzipFooter = |
||||||
|
{ |
||||||
|
(byte) crcval, (byte) (crcval >> 8), |
||||||
|
(byte) (crcval >> 16), (byte) (crcval >> 24), |
||||||
|
|
||||||
|
(byte) totalin, (byte) (totalin >> 8), |
||||||
|
(byte) (totalin >> 16), (byte) (totalin >> 24) |
||||||
|
}; |
||||||
|
|
||||||
|
out.write(gzipFooter); |
||||||
|
// System.err.println("wrote GZIP trailer (" + gzipFooter.length + " bytes )");
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,681 @@ |
|||||||
|
/* 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/* 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. |
||||||
|
* <p> |
||||||
|
* The usage is as following. First you have to set some input with |
||||||
|
* <code>setInput()</code>, then inflate() it. If inflate doesn't |
||||||
|
* inflate any bytes there may be three reasons: |
||||||
|
* <ul> |
||||||
|
* <li>needsInput() returns true because the input buffer is empty. |
||||||
|
* You have to provide more input with <code>setInput()</code>. |
||||||
|
* NOTE: needsInput() also returns true when, the stream is finished. |
||||||
|
* </li> |
||||||
|
* <li>needsDictionary() returns true, you have to provide a preset |
||||||
|
* dictionary with <code>setDictionary()</code>.</li> |
||||||
|
* <li>finished() returns true, the inflater has finished.</li> |
||||||
|
* </ul> |
||||||
|
* 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. <br> |
||||||
|
* <p> |
||||||
|
* 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 int 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 int 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 |
||||||
|
* <i>undefined</i>. |
||||||
|
* |
||||||
|
* @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 totalIn - getRemaining(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the total number of output bytes returned by inflate(). |
||||||
|
* |
||||||
|
* @return the total number of output bytes. |
||||||
|
*/ |
||||||
|
public int getTotalOut() { |
||||||
|
return totalOut; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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. |
||||||
|
* @throws DataFormatException if deflated stream is invalid. |
||||||
|
* @throws 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. |
||||||
|
* @throws DataFormatException if deflated stream is invalid. |
||||||
|
* @throws 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(). <br> |
||||||
|
* |
||||||
|
* <em>NOTE</em>: 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. |
||||||
|
* @throws IllegalStateException if no dictionary is needed. |
||||||
|
* @throws 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. |
||||||
|
* @throws IllegalStateException if no dictionary is needed. |
||||||
|
* @throws IllegalArgumentException if the dictionary checksum is |
||||||
|
* wrong. |
||||||
|
* @throws 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. |
||||||
|
* @throws 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. |
||||||
|
* @throws IllegalStateException if no input is needed. |
||||||
|
* @throws 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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decodes the deflate header. |
||||||
|
* |
||||||
|
* @return false if more input is needed. |
||||||
|
* @throws 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) != (Deflater.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. |
||||||
|
* @throws 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. |
||||||
|
* @throws 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. |
||||||
|
* @throws 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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,187 @@ |
|||||||
|
/* me.ag2s.epublib.zip.InflaterDynHeader |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,187 @@ |
|||||||
|
/* me.ag2s.epublib.zip.InflaterHuffmanTree |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
public 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 InternalError |
||||||
|
("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[DeflaterHuffman.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 = DeflaterHuffman.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)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
/* InflaterInputStream.java - Input stream filter for decompressing |
||||||
|
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 |
||||||
|
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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.FilterInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* This filter stream is used to decompress data compressed in the "deflate" |
||||||
|
* format. The "deflate" format is described in RFC 1951. |
||||||
|
* <p> |
||||||
|
* This stream may form the basis for other decompression filters, such |
||||||
|
* as the <code>GZIPInputStream</code>. |
||||||
|
* |
||||||
|
* @author John Leuner |
||||||
|
* @author Tom Tromey |
||||||
|
* @since 1.1 |
||||||
|
*/ |
||||||
|
public class InflaterInputStream extends FilterInputStream { |
||||||
|
/** |
||||||
|
* Decompressor for this filter |
||||||
|
*/ |
||||||
|
protected Inflater inf; |
||||||
|
|
||||||
|
/** |
||||||
|
* Byte array used as a buffer |
||||||
|
*/ |
||||||
|
protected byte[] buf; |
||||||
|
|
||||||
|
/** |
||||||
|
* Size of buffer |
||||||
|
*/ |
||||||
|
protected int len; |
||||||
|
|
||||||
|
/* |
||||||
|
* We just use this if we are decoding one byte at a time with the read() call |
||||||
|
*/ |
||||||
|
private byte[] onebytebuffer = new byte[1]; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InflaterInputStream with the default decompresseor |
||||||
|
* and a default buffer size. |
||||||
|
* |
||||||
|
* @param in the InputStream to read bytes from |
||||||
|
*/ |
||||||
|
public InflaterInputStream(InputStream in) { |
||||||
|
this(in, new Inflater(), 4096); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InflaterInputStream with the specified decompresseor |
||||||
|
* and a default buffer size. |
||||||
|
* |
||||||
|
* @param in the InputStream to read bytes from |
||||||
|
* @param inf the decompressor used to decompress data read from in |
||||||
|
*/ |
||||||
|
public InflaterInputStream(InputStream in, Inflater inf) { |
||||||
|
this(in, inf, 4096); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an InflaterInputStream with the specified decompresseor |
||||||
|
* and a specified buffer size. |
||||||
|
* |
||||||
|
* @param in the InputStream to read bytes from |
||||||
|
* @param inf the decompressor used to decompress data read from in |
||||||
|
* @param size size of the buffer to use |
||||||
|
*/ |
||||||
|
public InflaterInputStream(InputStream in, Inflater inf, int size) { |
||||||
|
super(in); |
||||||
|
this.len = 0; |
||||||
|
|
||||||
|
if (in == null) |
||||||
|
throw new NullPointerException("in may not be null"); |
||||||
|
if (inf == null) |
||||||
|
throw new NullPointerException("inf may not be null"); |
||||||
|
if (size < 0) |
||||||
|
throw new IllegalArgumentException("size may not be negative"); |
||||||
|
|
||||||
|
this.inf = inf; |
||||||
|
this.buf = new byte[size]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns 0 once the end of the stream (EOF) has been reached. |
||||||
|
* Otherwise returns 1. |
||||||
|
*/ |
||||||
|
public int available() throws IOException { |
||||||
|
// According to the JDK 1.2 docs, this should only ever return 0
|
||||||
|
// or 1 and should not be relied upon by Java programs.
|
||||||
|
return inf.finished() ? 0 : 1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the input stream |
||||||
|
*/ |
||||||
|
public synchronized void close() throws IOException { |
||||||
|
if (in != null) |
||||||
|
in.close(); |
||||||
|
in = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Fills the buffer with more data to decompress. |
||||||
|
*/ |
||||||
|
protected void fill() throws IOException { |
||||||
|
if (in == null) |
||||||
|
throw new ZipException("InflaterInputStream is closed"); |
||||||
|
|
||||||
|
len = in.read(buf, 0, buf.length); |
||||||
|
|
||||||
|
if (len < 0) |
||||||
|
throw new ZipException("Deflated stream ends early."); |
||||||
|
|
||||||
|
inf.setInput(buf, 0, len); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads one byte of decompressed data. |
||||||
|
* <p> |
||||||
|
* The byte is in the lower 8 bits of the int. |
||||||
|
*/ |
||||||
|
public int read() throws IOException { |
||||||
|
int nread = read(onebytebuffer, 0, 1); //read one byte
|
||||||
|
|
||||||
|
if (nread > 0) |
||||||
|
return onebytebuffer[0] & 0xff; |
||||||
|
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decompresses data into the byte array |
||||||
|
* |
||||||
|
* @param b the array to read and decompress data into |
||||||
|
* @param off the offset indicating where the data should be placed |
||||||
|
* @param len the number of bytes to decompress |
||||||
|
*/ |
||||||
|
public int read(byte[] b, int off, int len) throws IOException { |
||||||
|
if (len == 0) |
||||||
|
return 0; |
||||||
|
|
||||||
|
for (; ; ) { |
||||||
|
int count; |
||||||
|
|
||||||
|
try { |
||||||
|
count = inf.inflate(b, off, len); |
||||||
|
} catch (DataFormatException dfe) { |
||||||
|
throw new ZipException(dfe.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
if (count > 0) |
||||||
|
return count; |
||||||
|
|
||||||
|
if (inf.needsDictionary() |
||||||
|
| inf.finished()) |
||||||
|
return -1; |
||||||
|
else if (inf.needsInput()) |
||||||
|
fill(); |
||||||
|
else |
||||||
|
throw new InternalError("Don't know what to do"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Skip specified number of bytes of uncompressed data |
||||||
|
* |
||||||
|
* @param n number of bytes to skip |
||||||
|
*/ |
||||||
|
public long skip(long n) throws IOException { |
||||||
|
if (n < 0) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
|
||||||
|
if (n == 0) |
||||||
|
return 0; |
||||||
|
|
||||||
|
// Implementation copied from InputStream
|
||||||
|
// Throw away n bytes by reading them into a temp byte[].
|
||||||
|
// Limit the temp array to 2Kb so we don't grab too much memory.
|
||||||
|
final int buflen = n > 2048 ? 2048 : (int) n; |
||||||
|
byte[] tmpbuf = new byte[buflen]; |
||||||
|
final long origN = n; |
||||||
|
|
||||||
|
while (n > 0L) { |
||||||
|
int numread = read(tmpbuf, 0, n > buflen ? buflen : (int) n); |
||||||
|
if (numread <= 0) |
||||||
|
break; |
||||||
|
n -= numread; |
||||||
|
} |
||||||
|
|
||||||
|
return origN - n; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,161 @@ |
|||||||
|
/* me.ag2s.epublib.zip.OutputWindow |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/* |
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,181 @@ |
|||||||
|
/* me.ag2s.epublib.zip.PendingBuffer |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is general purpose class for writing data to a buffer. |
||||||
|
* <p> |
||||||
|
* It allows you to write bits as well as bytes |
||||||
|
* <p> |
||||||
|
* Based on DeflaterPending.java |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @date Jan 5, 2000 |
||||||
|
*/ |
||||||
|
|
||||||
|
class PendingBuffer { |
||||||
|
protected byte[] buf; |
||||||
|
int start; |
||||||
|
int end; |
||||||
|
|
||||||
|
int bits; |
||||||
|
int bitCount; |
||||||
|
|
||||||
|
public PendingBuffer() { |
||||||
|
this(4096); |
||||||
|
} |
||||||
|
|
||||||
|
public PendingBuffer(int bufsize) { |
||||||
|
buf = new byte[bufsize]; |
||||||
|
} |
||||||
|
|
||||||
|
public final void reset() { |
||||||
|
start = end = bitCount = 0; |
||||||
|
} |
||||||
|
|
||||||
|
public final void writeByte(int b) { |
||||||
|
if (DeflaterConstants.DEBUGGING && start != 0) |
||||||
|
throw new IllegalStateException(); |
||||||
|
buf[end++] = (byte) b; |
||||||
|
} |
||||||
|
|
||||||
|
public final void writeShort(int s) { |
||||||
|
if (DeflaterConstants.DEBUGGING && start != 0) |
||||||
|
throw new IllegalStateException(); |
||||||
|
buf[end++] = (byte) s; |
||||||
|
buf[end++] = (byte) (s >> 8); |
||||||
|
} |
||||||
|
|
||||||
|
public final void writeInt(int s) { |
||||||
|
if (DeflaterConstants.DEBUGGING && start != 0) |
||||||
|
throw new IllegalStateException(); |
||||||
|
buf[end++] = (byte) s; |
||||||
|
buf[end++] = (byte) (s >> 8); |
||||||
|
buf[end++] = (byte) (s >> 16); |
||||||
|
buf[end++] = (byte) (s >> 24); |
||||||
|
} |
||||||
|
|
||||||
|
public final void writeBlock(byte[] block, int offset, int len) { |
||||||
|
if (DeflaterConstants.DEBUGGING && start != 0) |
||||||
|
throw new IllegalStateException(); |
||||||
|
System.arraycopy(block, offset, buf, end, len); |
||||||
|
end += len; |
||||||
|
} |
||||||
|
|
||||||
|
public final int getBitCount() { |
||||||
|
return bitCount; |
||||||
|
} |
||||||
|
|
||||||
|
public final void alignToByte() { |
||||||
|
if (DeflaterConstants.DEBUGGING && start != 0) |
||||||
|
throw new IllegalStateException(); |
||||||
|
if (bitCount > 0) { |
||||||
|
buf[end++] = (byte) bits; |
||||||
|
if (bitCount > 8) |
||||||
|
buf[end++] = (byte) (bits >>> 8); |
||||||
|
} |
||||||
|
bits = 0; |
||||||
|
bitCount = 0; |
||||||
|
} |
||||||
|
|
||||||
|
public final void writeBits(int b, int count) { |
||||||
|
if (DeflaterConstants.DEBUGGING && start != 0) |
||||||
|
throw new IllegalStateException(); |
||||||
|
if (DeflaterConstants.DEBUGGING) |
||||||
|
System.err.println("writeBits(" + Integer.toHexString(b) + "," + count + ")"); |
||||||
|
bits |= b << bitCount; |
||||||
|
bitCount += count; |
||||||
|
if (bitCount >= 16) { |
||||||
|
buf[end++] = (byte) bits; |
||||||
|
buf[end++] = (byte) (bits >>> 8); |
||||||
|
bits >>>= 16; |
||||||
|
bitCount -= 16; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final void writeShortMSB(int s) { |
||||||
|
if (DeflaterConstants.DEBUGGING && start != 0) |
||||||
|
throw new IllegalStateException(); |
||||||
|
buf[end++] = (byte) (s >> 8); |
||||||
|
buf[end++] = (byte) s; |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean isFlushed() { |
||||||
|
return end == 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Flushes the pending buffer into the given output array. If the |
||||||
|
* output array is to small, only a partial flush is done. |
||||||
|
* |
||||||
|
* @param output the output array; |
||||||
|
* @param offset the offset into output array; |
||||||
|
* @param length the maximum number of bytes to store; |
||||||
|
* @throws IndexOutOfBoundsException if offset or length are |
||||||
|
* invalid. |
||||||
|
*/ |
||||||
|
public final int flush(byte[] output, int offset, int length) { |
||||||
|
if (bitCount >= 8) { |
||||||
|
buf[end++] = (byte) bits; |
||||||
|
bits >>>= 8; |
||||||
|
bitCount -= 8; |
||||||
|
} |
||||||
|
if (length > end - start) { |
||||||
|
length = end - start; |
||||||
|
System.arraycopy(buf, start, output, offset, length); |
||||||
|
start = 0; |
||||||
|
end = 0; |
||||||
|
} else { |
||||||
|
System.arraycopy(buf, start, output, offset, length); |
||||||
|
start += length; |
||||||
|
} |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public final byte[] toByteArray() { |
||||||
|
byte[] ret = new byte[end - start]; |
||||||
|
System.arraycopy(buf, start, ret, 0, ret.length); |
||||||
|
start = 0; |
||||||
|
end = 0; |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,206 @@ |
|||||||
|
/* me.ag2s.epublib.zip.StreamManipulator |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class allows us to retrieve a specified amount of bits from |
||||||
|
* the input buffer, as well as copy big byte blocks. |
||||||
|
* <p> |
||||||
|
* 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. |
||||||
|
* <p> |
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,96 @@ |
|||||||
|
/* me.ag2s.epublib.zip.ZipConstants |
||||||
|
Copyright (C) 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 thata |
||||||
|
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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
interface ZipConstants { |
||||||
|
/* The local file header */ |
||||||
|
int LOCHDR = 30; |
||||||
|
int LOCSIG = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); |
||||||
|
|
||||||
|
int LOCVER = 4; |
||||||
|
int LOCFLG = 6; |
||||||
|
int LOCHOW = 8; |
||||||
|
int LOCTIM = 10; |
||||||
|
int LOCCRC = 14; |
||||||
|
int LOCSIZ = 18; |
||||||
|
int LOCLEN = 22; |
||||||
|
int LOCNAM = 26; |
||||||
|
int LOCEXT = 28; |
||||||
|
|
||||||
|
/* The Data descriptor */ |
||||||
|
int EXTSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); |
||||||
|
int EXTHDR = 16; |
||||||
|
|
||||||
|
int EXTCRC = 4; |
||||||
|
int EXTSIZ = 8; |
||||||
|
int EXTLEN = 12; |
||||||
|
|
||||||
|
/* The central directory file header */ |
||||||
|
int CENSIG = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); |
||||||
|
int CENHDR = 46; |
||||||
|
|
||||||
|
int CENVEM = 4; |
||||||
|
int CENVER = 6; |
||||||
|
int CENFLG = 8; |
||||||
|
int CENHOW = 10; |
||||||
|
int CENTIM = 12; |
||||||
|
int CENCRC = 16; |
||||||
|
int CENSIZ = 20; |
||||||
|
int CENLEN = 24; |
||||||
|
int CENNAM = 28; |
||||||
|
int CENEXT = 30; |
||||||
|
int CENCOM = 32; |
||||||
|
int CENDSK = 34; |
||||||
|
int CENATT = 36; |
||||||
|
int CENATX = 38; |
||||||
|
int CENOFF = 42; |
||||||
|
|
||||||
|
/* The entries in the end of central directory */ |
||||||
|
int ENDSIG = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); |
||||||
|
int ENDHDR = 22; |
||||||
|
|
||||||
|
/* The following two fields are missing in SUN JDK */ |
||||||
|
int ENDNRD = 4; |
||||||
|
int ENDDCD = 6; |
||||||
|
int ENDSUB = 8; |
||||||
|
int ENDTOT = 10; |
||||||
|
int ENDSIZ = 12; |
||||||
|
int ENDOFF = 16; |
||||||
|
int ENDCOM = 20; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,389 @@ |
|||||||
|
/* me.ag2s.epublib.zip.ZipEntry |
||||||
|
Copyright (C) 2001, 2002 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.util.Calendar; |
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents a member of a zip archive. ZipFile and |
||||||
|
* ZipInputStream will give you instances of this class as information |
||||||
|
* about the members in an archive. On the other hand ZipOutputStream |
||||||
|
* needs an instance of this class to create a new member. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class ZipEntry implements ZipConstants, Cloneable { |
||||||
|
private static final int KNOWN_SIZE = 1; |
||||||
|
private static final int KNOWN_CSIZE = 2; |
||||||
|
private static final int KNOWN_CRC = 4; |
||||||
|
private static final int KNOWN_TIME = 8; |
||||||
|
|
||||||
|
private static Calendar cal; |
||||||
|
|
||||||
|
private final String name; |
||||||
|
private int size; |
||||||
|
private int compressedSize; |
||||||
|
private int crc; |
||||||
|
private int dostime; |
||||||
|
private short known = 0; |
||||||
|
private short method = -1; |
||||||
|
private byte[] extra = null; |
||||||
|
private String comment = null; |
||||||
|
|
||||||
|
int flags; /* used by ZipOutputStream */ |
||||||
|
int offset; /* used by ZipFile and ZipOutputStream */ |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Compression method. This method doesn't compress at all. |
||||||
|
*/ |
||||||
|
public final static int STORED = 0; |
||||||
|
/** |
||||||
|
* Compression method. This method uses the Deflater. |
||||||
|
*/ |
||||||
|
public final static int DEFLATED = 8; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a zip entry with the given name. |
||||||
|
* |
||||||
|
* @param name the name. May include directory components separated |
||||||
|
* by '/'. |
||||||
|
* @throws NullPointerException when name is null. |
||||||
|
* @throws IllegalArgumentException when name is bigger then 65535 chars. |
||||||
|
*/ |
||||||
|
public ZipEntry(String name) { |
||||||
|
int length = name.length(); |
||||||
|
if (length > 65535) |
||||||
|
throw new IllegalArgumentException("name length is " + length); |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a copy of the given zip entry. |
||||||
|
* |
||||||
|
* @param e the entry to copy. |
||||||
|
*/ |
||||||
|
public ZipEntry(ZipEntry e) { |
||||||
|
name = e.name; |
||||||
|
known = e.known; |
||||||
|
size = e.size; |
||||||
|
compressedSize = e.compressedSize; |
||||||
|
crc = e.crc; |
||||||
|
dostime = e.dostime; |
||||||
|
method = e.method; |
||||||
|
extra = e.extra; |
||||||
|
comment = e.comment; |
||||||
|
} |
||||||
|
|
||||||
|
final void setDOSTime(int dostime) { |
||||||
|
this.dostime = dostime; |
||||||
|
known |= KNOWN_TIME; |
||||||
|
} |
||||||
|
|
||||||
|
final int getDOSTime() { |
||||||
|
if ((known & KNOWN_TIME) == 0) |
||||||
|
return 0; |
||||||
|
else |
||||||
|
return dostime; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a copy of this zip entry. |
||||||
|
*/ |
||||||
|
/** |
||||||
|
* Clones the entry. |
||||||
|
*/ |
||||||
|
public Object clone() { |
||||||
|
try { |
||||||
|
// The JCL says that the `extra' field is also copied.
|
||||||
|
ZipEntry clone = (ZipEntry) super.clone(); |
||||||
|
if (extra != null) |
||||||
|
clone.extra = (byte[]) extra.clone(); |
||||||
|
return clone; |
||||||
|
} catch (CloneNotSupportedException ex) { |
||||||
|
throw new InternalError(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the entry name. The path components in the entry are |
||||||
|
* always separated by slashes ('/'). |
||||||
|
*/ |
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the time of last modification of the entry. |
||||||
|
* |
||||||
|
* @time the time of last modification of the entry. |
||||||
|
*/ |
||||||
|
public void setTime(long time) { |
||||||
|
Calendar cal = getCalendar(); |
||||||
|
synchronized (cal) { |
||||||
|
cal.setTime(new Date(time * 1000L)); |
||||||
|
dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25 |
||||||
|
| (cal.get(Calendar.MONTH) + 1) << 21 |
||||||
|
| (cal.get(Calendar.DAY_OF_MONTH)) << 16 |
||||||
|
| (cal.get(Calendar.HOUR_OF_DAY)) << 11 |
||||||
|
| (cal.get(Calendar.MINUTE)) << 5 |
||||||
|
| (cal.get(Calendar.SECOND)) >> 1; |
||||||
|
} |
||||||
|
dostime = (int) (dostime / 1000L); |
||||||
|
this.known |= KNOWN_TIME; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the time of last modification of the entry. |
||||||
|
* |
||||||
|
* @return the time of last modification of the entry, or -1 if unknown. |
||||||
|
*/ |
||||||
|
public long getTime() { |
||||||
|
if ((known & KNOWN_TIME) == 0) |
||||||
|
return -1; |
||||||
|
|
||||||
|
int sec = 2 * (dostime & 0x1f); |
||||||
|
int min = (dostime >> 5) & 0x3f; |
||||||
|
int hrs = (dostime >> 11) & 0x1f; |
||||||
|
int day = (dostime >> 16) & 0x1f; |
||||||
|
int mon = ((dostime >> 21) & 0xf) - 1; |
||||||
|
int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */ |
||||||
|
|
||||||
|
try { |
||||||
|
cal = getCalendar(); |
||||||
|
synchronized (cal) { |
||||||
|
cal.set(year, mon, day, hrs, min, sec); |
||||||
|
return cal.getTime().getTime(); |
||||||
|
} |
||||||
|
} catch (RuntimeException ex) { |
||||||
|
/* Ignore illegal time stamp */ |
||||||
|
known &= ~KNOWN_TIME; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static synchronized Calendar getCalendar() { |
||||||
|
if (cal == null) |
||||||
|
cal = Calendar.getInstance(); |
||||||
|
|
||||||
|
return cal; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the size of the uncompressed data. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if size is not in 0..0xffffffffL |
||||||
|
*/ |
||||||
|
public void setSize(long size) { |
||||||
|
if ((size & 0xffffffff00000000L) != 0) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
this.size = (int) size; |
||||||
|
this.known |= KNOWN_SIZE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the size of the uncompressed data. |
||||||
|
* |
||||||
|
* @return the size or -1 if unknown. |
||||||
|
*/ |
||||||
|
public long getSize() { |
||||||
|
return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the size of the compressed data. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if size is not in 0..0xffffffffL |
||||||
|
*/ |
||||||
|
public void setCompressedSize(long csize) { |
||||||
|
if ((csize & 0xffffffff00000000L) != 0) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
this.compressedSize = (int) csize; |
||||||
|
this.known |= KNOWN_CSIZE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the size of the compressed data. |
||||||
|
* |
||||||
|
* @return the size or -1 if unknown. |
||||||
|
*/ |
||||||
|
public long getCompressedSize() { |
||||||
|
return (known & KNOWN_CSIZE) != 0 ? compressedSize & 0xffffffffL : -1L; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the crc of the uncompressed data. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if crc is not in 0..0xffffffffL |
||||||
|
*/ |
||||||
|
public void setCrc(long crc) { |
||||||
|
if ((crc & 0xffffffff00000000L) != 0) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
this.crc = (int) crc; |
||||||
|
this.known |= KNOWN_CRC; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the crc of the uncompressed data. |
||||||
|
* |
||||||
|
* @return the crc or -1 if unknown. |
||||||
|
*/ |
||||||
|
public long getCrc() { |
||||||
|
return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the compression method. Only DEFLATED and STORED are |
||||||
|
* supported. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if method is not supported. |
||||||
|
* @see ZipOutputStream#DEFLATED |
||||||
|
* @see ZipOutputStream#STORED |
||||||
|
*/ |
||||||
|
public void setMethod(int method) { |
||||||
|
if (method != ZipOutputStream.STORED |
||||||
|
&& method != ZipOutputStream.DEFLATED) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
this.method = (short) method; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the compression method. |
||||||
|
* |
||||||
|
* @return the compression method or -1 if unknown. |
||||||
|
*/ |
||||||
|
public int getMethod() { |
||||||
|
return method; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the extra data. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if extra is longer than 0xffff bytes. |
||||||
|
*/ |
||||||
|
public void setExtra(byte[] extra) { |
||||||
|
if (extra == null) { |
||||||
|
this.extra = null; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (extra.length > 0xffff) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
this.extra = extra; |
||||||
|
try { |
||||||
|
int pos = 0; |
||||||
|
while (pos < extra.length) { |
||||||
|
int sig = (extra[pos++] & 0xff) |
||||||
|
| (extra[pos++] & 0xff) << 8; |
||||||
|
int len = (extra[pos++] & 0xff) |
||||||
|
| (extra[pos++] & 0xff) << 8; |
||||||
|
if (sig == 0x5455) { |
||||||
|
/* extended time stamp */ |
||||||
|
int flags = extra[pos]; |
||||||
|
if ((flags & 1) != 0) { |
||||||
|
long time = ((extra[pos + 1] & 0xff) |
||||||
|
| (extra[pos + 2] & 0xff) << 8 |
||||||
|
| (extra[pos + 3] & 0xff) << 16 |
||||||
|
| (extra[pos + 4] & 0xff) << 24); |
||||||
|
setTime(time); |
||||||
|
} |
||||||
|
} |
||||||
|
pos += len; |
||||||
|
} |
||||||
|
} catch (ArrayIndexOutOfBoundsException ex) { |
||||||
|
/* be lenient */ |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the extra data. |
||||||
|
* |
||||||
|
* @return the extra data or null if not set. |
||||||
|
*/ |
||||||
|
public byte[] getExtra() { |
||||||
|
return extra; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the entry comment. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if comment is longer than 0xffff. |
||||||
|
*/ |
||||||
|
public void setComment(String comment) { |
||||||
|
if (comment != null && comment.length() > 0xffff) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
this.comment = comment; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the comment. |
||||||
|
* |
||||||
|
* @return the comment or null if not set. |
||||||
|
*/ |
||||||
|
public String getComment() { |
||||||
|
return comment; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets true, if the entry is a directory. This is solely |
||||||
|
* determined by the name, a trailing slash '/' marks a directory. |
||||||
|
*/ |
||||||
|
public boolean isDirectory() { |
||||||
|
int nlen = name.length(); |
||||||
|
return nlen > 0 && name.charAt(nlen - 1) == '/'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the string representation of this ZipEntry. This is just |
||||||
|
* the name as returned by getName(). |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the hashCode of this ZipEntry. This is just the hashCode |
||||||
|
* of the name. Note that the equals method isn't changed, though. |
||||||
|
*/ |
||||||
|
public int hashCode() { |
||||||
|
return name.hashCode(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
/* ZipException.java - exception representing a zip related error |
||||||
|
Copyright (C) 1998, 1999, 2000, 2001, 2002 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Thrown during the creation or input of a zip file. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @author Per Bothner |
||||||
|
* @status updated to 1.4 |
||||||
|
*/ |
||||||
|
public class ZipException extends IOException { |
||||||
|
/** |
||||||
|
* Compatible with JDK 1.0+. |
||||||
|
*/ |
||||||
|
private static final long serialVersionUID = 8000196834066748623L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an exception without a message. |
||||||
|
*/ |
||||||
|
public ZipException() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create an exception with a message. |
||||||
|
* |
||||||
|
* @param msg the message |
||||||
|
*/ |
||||||
|
public ZipException(String msg) { |
||||||
|
super(msg); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,526 @@ |
|||||||
|
/* me.ag2s.epublib.zip.ZipFile |
||||||
|
Copyright (C) 2001, 2002, 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.net.Uri; |
||||||
|
import android.system.ErrnoException; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
|
||||||
|
import java.io.BufferedInputStream; |
||||||
|
import java.io.Closeable; |
||||||
|
import java.io.DataInput; |
||||||
|
import java.io.EOFException; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.util.Enumeration; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.NoSuchElementException; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents a Zip archive. You can ask for the contained |
||||||
|
* entries, or get an input stream for a file entry. The entry is |
||||||
|
* automatically decompressed. |
||||||
|
* <p> |
||||||
|
* This class is thread safe: You can open input streams for arbitrary |
||||||
|
* entries in different threads. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @author Artur Biesiadowski |
||||||
|
*/ |
||||||
|
public class ZipFile implements ZipConstants, Closeable { |
||||||
|
|
||||||
|
/** |
||||||
|
* Mode flag to open a zip file for reading. |
||||||
|
*/ |
||||||
|
public static final int OPEN_READ = 0x1; |
||||||
|
|
||||||
|
/** |
||||||
|
* Mode flag to delete a zip file after reading. |
||||||
|
*/ |
||||||
|
public static final int OPEN_DELETE = 0x4; |
||||||
|
|
||||||
|
@NonNull |
||||||
|
public Context getContext() { |
||||||
|
return context; |
||||||
|
} |
||||||
|
|
||||||
|
public Uri getUri() { |
||||||
|
return Uri.parse(name); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@NonNull |
||||||
|
private final Context context; |
||||||
|
|
||||||
|
// Name of this zip file.
|
||||||
|
@NonNull |
||||||
|
private final String name; |
||||||
|
|
||||||
|
// File from which zip entries are read.
|
||||||
|
@NonNull |
||||||
|
private final /*RandomAccessFile*/ AndroidRandomReadableFile araf; |
||||||
|
|
||||||
|
|
||||||
|
// The entries of this zip file when initialized and not yet closed.
|
||||||
|
private HashMap<String, ZipEntry> entries; |
||||||
|
|
||||||
|
private boolean closed = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Zip file with the given name for reading. |
||||||
|
* |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws ZipException if the file doesn't contain a valid zip |
||||||
|
* archive. |
||||||
|
*/ |
||||||
|
public ZipFile(@NonNull Context context, @NonNull Uri uri) throws ZipException, IOException { |
||||||
|
this.context = context; |
||||||
|
this.araf = new AndroidRandomReadableFile(context, uri); |
||||||
|
this.name = uri.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Opens a Zip file reading the given File in the given mode. |
||||||
|
* |
||||||
|
* If the OPEN_DELETE mode is specified, the zip file will be deleted at |
||||||
|
* some time moment after it is opened. It will be deleted before the zip |
||||||
|
* file is closed or the Virtual Machine exits. |
||||||
|
* |
||||||
|
* The contents of the zip file will be accessible until it is closed. |
||||||
|
* |
||||||
|
* The OPEN_DELETE mode is currently unimplemented in this library |
||||||
|
* |
||||||
|
* @since JDK1.3 |
||||||
|
* @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE |
||||||
|
* |
||||||
|
* @exception IOException if a i/o error occured. |
||||||
|
* @exception ZipException if the file doesn't contain a valid zip |
||||||
|
* archive. |
||||||
|
*/ |
||||||
|
// public ZipFile(File file, int mode) throws ZipException, IOException
|
||||||
|
// {
|
||||||
|
// if ((mode & OPEN_DELETE) != 0)
|
||||||
|
// {
|
||||||
|
// throw new IllegalArgumentException
|
||||||
|
// ("OPEN_DELETE mode not supported yet in me.ag2s.epublib.zip.ZipFile");
|
||||||
|
// }
|
||||||
|
// this.raf = new RandomAccessFile(file, "r");
|
||||||
|
// this.name = file.getPath();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** |
||||||
|
* Read an unsigned short in little endian byte order from the given |
||||||
|
* DataInput stream using the given byte buffer. |
||||||
|
* |
||||||
|
* @param di DataInput stream to read from. |
||||||
|
* @param b the byte buffer to read in (must be at least 2 bytes long). |
||||||
|
* @return The value read. |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws EOFException if the file ends prematurely |
||||||
|
*/ |
||||||
|
private final int readLeShort(DataInput di, byte[] b) throws IOException { |
||||||
|
di.readFully(b, 0, 2); |
||||||
|
return (b[0] & 0xff) | (b[1] & 0xff) << 8; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read an int in little endian byte order from the given |
||||||
|
* DataInput stream using the given byte buffer. |
||||||
|
* |
||||||
|
* @param di DataInput stream to read from. |
||||||
|
* @param b the byte buffer to read in (must be at least 4 bytes long). |
||||||
|
* @return The value read. |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws EOFException if the file ends prematurely |
||||||
|
*/ |
||||||
|
private final int readLeInt(DataInput di, byte[] b) throws IOException { |
||||||
|
di.readFully(b, 0, 4); |
||||||
|
return ((b[0] & 0xff) | (b[1] & 0xff) << 8) |
||||||
|
| ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Read an unsigned short in little endian byte order from the given |
||||||
|
* byte buffer at the given offset. |
||||||
|
* |
||||||
|
* @param b the byte array to read from. |
||||||
|
* @param off the offset to read from. |
||||||
|
* @return The value read. |
||||||
|
*/ |
||||||
|
private final int readLeShort(byte[] b, int off) { |
||||||
|
return (b[off] & 0xff) | (b[off + 1] & 0xff) << 8; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read an int in little endian byte order from the given |
||||||
|
* byte buffer at the given offset. |
||||||
|
* |
||||||
|
* @param b the byte array to read from. |
||||||
|
* @param off the offset to read from. |
||||||
|
* @return The value read. |
||||||
|
*/ |
||||||
|
private final int readLeInt(byte[] b, int off) { |
||||||
|
return ((b[off] & 0xff) | (b[off + 1] & 0xff) << 8) |
||||||
|
| ((b[off + 2] & 0xff) | (b[off + 3] & 0xff) << 8) << 16; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Read the central directory of a zip file and fill the entries |
||||||
|
* array. This is called exactly once when first needed. It is called |
||||||
|
* while holding the lock on <code>raf</code>. |
||||||
|
* |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws ZipException if the central directory is malformed |
||||||
|
*/ |
||||||
|
private void readEntries() throws ZipException, IOException, ErrnoException { |
||||||
|
/* Search for the End Of Central Directory. When a zip comment is |
||||||
|
* present the directory may start earlier. |
||||||
|
* FIXME: This searches the whole file in a very slow manner if the |
||||||
|
* file isn't a zip file. |
||||||
|
*/ |
||||||
|
long pos = araf.length() - ENDHDR; |
||||||
|
byte[] ebs = new byte[CENHDR]; |
||||||
|
|
||||||
|
do { |
||||||
|
if (pos < 0) |
||||||
|
throw new ZipException |
||||||
|
("central directory not found, probably not a zip file: " + name); |
||||||
|
araf.seek(pos--); |
||||||
|
} |
||||||
|
while (readLeInt(araf, ebs) != ENDSIG); |
||||||
|
|
||||||
|
if (araf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD) |
||||||
|
throw new EOFException(name); |
||||||
|
int count = readLeShort(araf, ebs); |
||||||
|
if (araf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ) |
||||||
|
throw new EOFException(name); |
||||||
|
int centralOffset = readLeInt(araf, ebs); |
||||||
|
|
||||||
|
entries = new HashMap<>(count + count / 2); |
||||||
|
araf.seek(centralOffset); |
||||||
|
|
||||||
|
byte[] buffer = new byte[16]; |
||||||
|
for (int i = 0; i < count; i++) { |
||||||
|
araf.readFully(ebs); |
||||||
|
if (readLeInt(ebs, 0) != CENSIG) |
||||||
|
throw new ZipException("Wrong Central Directory signature: " + name); |
||||||
|
|
||||||
|
int method = readLeShort(ebs, CENHOW); |
||||||
|
int dostime = readLeInt(ebs, CENTIM); |
||||||
|
int crc = readLeInt(ebs, CENCRC); |
||||||
|
int csize = readLeInt(ebs, CENSIZ); |
||||||
|
int size = readLeInt(ebs, CENLEN); |
||||||
|
int nameLen = readLeShort(ebs, CENNAM); |
||||||
|
int extraLen = readLeShort(ebs, CENEXT); |
||||||
|
int commentLen = readLeShort(ebs, CENCOM); |
||||||
|
|
||||||
|
int offset = readLeInt(ebs, CENOFF); |
||||||
|
|
||||||
|
int needBuffer = Math.max(nameLen, commentLen); |
||||||
|
if (buffer.length < needBuffer) |
||||||
|
buffer = new byte[needBuffer]; |
||||||
|
|
||||||
|
araf.readFully(buffer, 0, nameLen); |
||||||
|
String name = new String(buffer, 0, 0, nameLen); |
||||||
|
|
||||||
|
ZipEntry entry = new ZipEntry(name); |
||||||
|
entry.setMethod(method); |
||||||
|
entry.setCrc(crc & 0xffffffffL); |
||||||
|
entry.setSize(size & 0xffffffffL); |
||||||
|
entry.setCompressedSize(csize & 0xffffffffL); |
||||||
|
entry.setDOSTime(dostime); |
||||||
|
if (extraLen > 0) { |
||||||
|
byte[] extra = new byte[extraLen]; |
||||||
|
araf.readFully(extra); |
||||||
|
entry.setExtra(extra); |
||||||
|
} |
||||||
|
if (commentLen > 0) { |
||||||
|
araf.readFully(buffer, 0, commentLen); |
||||||
|
entry.setComment(new String(buffer, 0, commentLen)); |
||||||
|
} |
||||||
|
entry.offset = offset; |
||||||
|
entries.put(name, entry); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the ZipFile. This also closes all input streams given by |
||||||
|
* this class. After this is called, no further method should be |
||||||
|
* called. |
||||||
|
* |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void close() throws IOException { |
||||||
|
synchronized (araf) { |
||||||
|
closed = true; |
||||||
|
entries = null; |
||||||
|
araf.close(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls the <code>close()</code> method when this ZipFile has not yet |
||||||
|
* been explicitly closed. |
||||||
|
*/ |
||||||
|
protected void finalize() throws IOException { |
||||||
|
if (!closed && araf != null) close(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns an enumeration of all Zip entries in this Zip file. |
||||||
|
*/ |
||||||
|
public Enumeration<? extends ZipEntry> entries() { |
||||||
|
try { |
||||||
|
return new ZipEntryEnumeration(getEntries().values().iterator()); |
||||||
|
} catch (IOException ioe) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks that the ZipFile is still open and reads entries when necessary. |
||||||
|
* |
||||||
|
* @throws IllegalStateException when the ZipFile has already been closed. |
||||||
|
* @throws IOException, ErrnoException when the entries could not be read. |
||||||
|
*/ |
||||||
|
private HashMap<String, ZipEntry> getEntries() throws IOException { |
||||||
|
synchronized (araf) { |
||||||
|
if (closed) |
||||||
|
throw new IllegalStateException("ZipFile has closed: " + name); |
||||||
|
|
||||||
|
if (entries == null) { |
||||||
|
try { |
||||||
|
readEntries(); |
||||||
|
} catch (ErrnoException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return entries; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Searches for a zip entry in this archive with the given name. |
||||||
|
* |
||||||
|
* @param name May contain directory components separated by |
||||||
|
* slashes ('/'). |
||||||
|
* @return the zip entry, or null if no entry with that name exists. |
||||||
|
*/ |
||||||
|
public ZipEntry getEntry(String name) { |
||||||
|
try { |
||||||
|
HashMap<String, ZipEntry> entries = getEntries(); |
||||||
|
ZipEntry entry = entries.get(name); |
||||||
|
return entry != null ? (ZipEntry) entry.clone() : null; |
||||||
|
} catch (IOException ioe) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//access should be protected by synchronized(raf)
|
||||||
|
private final byte[] locBuf = new byte[LOCHDR]; |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks, if the local header of the entry at index i matches the |
||||||
|
* central directory, and returns the offset to the data. |
||||||
|
* |
||||||
|
* @param entry to check. |
||||||
|
* @return the start offset of the (compressed) data. |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws ZipException if the local header doesn't match the |
||||||
|
* central directory header |
||||||
|
*/ |
||||||
|
private long checkLocalHeader(ZipEntry entry) throws IOException { |
||||||
|
synchronized (araf) { |
||||||
|
try { |
||||||
|
araf.seek(entry.offset); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
araf.readFully(locBuf); |
||||||
|
|
||||||
|
if (readLeInt(locBuf, 0) != LOCSIG) |
||||||
|
throw new ZipException("Wrong Local header signature: " + name); |
||||||
|
|
||||||
|
if (entry.getMethod() != readLeShort(locBuf, LOCHOW)) |
||||||
|
throw new ZipException("Compression method mismatch: " + name); |
||||||
|
|
||||||
|
if (entry.getName().length() != readLeShort(locBuf, LOCNAM)) |
||||||
|
throw new ZipException("file name length mismatch: " + name); |
||||||
|
|
||||||
|
int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT); |
||||||
|
return entry.offset + LOCHDR + extraLen; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an input stream reading the given zip entry as |
||||||
|
* uncompressed data. Normally zip entry should be an entry |
||||||
|
* returned by getEntry() or entries(). |
||||||
|
* |
||||||
|
* @param entry the entry to create an InputStream for. |
||||||
|
* @return the input stream. |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws ZipException if the Zip archive is malformed. |
||||||
|
*/ |
||||||
|
public InputStream getInputStream(ZipEntry entry) throws IOException { |
||||||
|
HashMap<String, ZipEntry> entries = getEntries(); |
||||||
|
String name = entry.getName(); |
||||||
|
ZipEntry zipEntry = entries.get(name); |
||||||
|
if (zipEntry == null) |
||||||
|
throw new NoSuchElementException(name); |
||||||
|
|
||||||
|
long start = checkLocalHeader(zipEntry); |
||||||
|
int method = zipEntry.getMethod(); |
||||||
|
InputStream is = new BufferedInputStream(new PartialInputStream |
||||||
|
(araf, start, zipEntry.getCompressedSize())); |
||||||
|
switch (method) { |
||||||
|
case ZipOutputStream.STORED: |
||||||
|
return is; |
||||||
|
case ZipOutputStream.DEFLATED: |
||||||
|
return new InflaterInputStream(is, new Inflater(true)); |
||||||
|
default: |
||||||
|
throw new ZipException("Unknown compression method " + method); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the (path) name of this zip file. |
||||||
|
*/ |
||||||
|
@NonNull |
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of entries in this zip file. |
||||||
|
*/ |
||||||
|
public int size() { |
||||||
|
try { |
||||||
|
return getEntries().size(); |
||||||
|
} catch (IOException ioe) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class ZipEntryEnumeration implements Enumeration<ZipEntry> { |
||||||
|
private final Iterator<ZipEntry> elements; |
||||||
|
|
||||||
|
public ZipEntryEnumeration(Iterator<ZipEntry> elements) { |
||||||
|
this.elements = elements; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasMoreElements() { |
||||||
|
return elements.hasNext(); |
||||||
|
} |
||||||
|
|
||||||
|
public ZipEntry nextElement() { |
||||||
|
/* We return a clone, just to be safe that the user doesn't |
||||||
|
* change the entry. |
||||||
|
*/ |
||||||
|
return (ZipEntry) elements.next().clone(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class PartialInputStream extends InputStream { |
||||||
|
private final /*RandomAccessFile*/ AndroidRandomReadableFile raf; |
||||||
|
long filepos, end; |
||||||
|
|
||||||
|
public PartialInputStream(AndroidRandomReadableFile raf, long start, long len) { |
||||||
|
this.raf = raf; |
||||||
|
filepos = start; |
||||||
|
end = start + len; |
||||||
|
} |
||||||
|
|
||||||
|
public int available() { |
||||||
|
long amount = end - filepos; |
||||||
|
if (amount > Integer.MAX_VALUE) |
||||||
|
return Integer.MAX_VALUE; |
||||||
|
return (int) amount; |
||||||
|
} |
||||||
|
|
||||||
|
public int read() throws IOException { |
||||||
|
if (filepos == end) |
||||||
|
return -1; |
||||||
|
synchronized (raf) { |
||||||
|
try { |
||||||
|
raf.seek(filepos++); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return raf.read(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException { |
||||||
|
if (len > end - filepos) { |
||||||
|
len = (int) (end - filepos); |
||||||
|
if (len == 0) |
||||||
|
return -1; |
||||||
|
} |
||||||
|
synchronized (raf) { |
||||||
|
try { |
||||||
|
raf.seek(filepos); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
int count = raf.read(b, off, len); |
||||||
|
if (count > 0) |
||||||
|
filepos += len; |
||||||
|
return count; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public long skip(long amount) { |
||||||
|
if (amount < 0) |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
if (amount > end - filepos) |
||||||
|
amount = end - filepos; |
||||||
|
filepos += amount; |
||||||
|
return amount; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,344 @@ |
|||||||
|
/* me.ag2s.epublib.zip.ZipInputStream |
||||||
|
Copyright (C) 2001, 2002, 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.EOFException; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is a FilterInputStream that reads the files in an zip archive |
||||||
|
* one after another. It has a special method to get the zip entry of |
||||||
|
* the next file. The zip entry contains information about the file name |
||||||
|
* size, compressed size, CRC, etc. |
||||||
|
* <p> |
||||||
|
* It includes support for STORED and DEFLATED entries. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class ZipInputStream extends InflaterInputStream implements ZipConstants { |
||||||
|
private CRC32 crc = new CRC32(); |
||||||
|
private ZipEntry entry = null; |
||||||
|
|
||||||
|
private int csize; |
||||||
|
private int size; |
||||||
|
private int method; |
||||||
|
private int flags; |
||||||
|
private int avail; |
||||||
|
private boolean entryAtEOF; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Zip input stream, reading a zip archive. |
||||||
|
*/ |
||||||
|
public ZipInputStream(InputStream in) { |
||||||
|
super(in, new Inflater(true)); |
||||||
|
} |
||||||
|
|
||||||
|
private void fillBuf() throws IOException { |
||||||
|
avail = len = in.read(buf, 0, buf.length); |
||||||
|
} |
||||||
|
|
||||||
|
private int readBuf(byte[] out, int offset, int length) throws IOException { |
||||||
|
if (avail <= 0) { |
||||||
|
fillBuf(); |
||||||
|
if (avail <= 0) |
||||||
|
return -1; |
||||||
|
} |
||||||
|
if (length > avail) |
||||||
|
length = avail; |
||||||
|
System.arraycopy(buf, len - avail, out, offset, length); |
||||||
|
avail -= length; |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
private void readFully(byte[] out) throws IOException { |
||||||
|
int off = 0; |
||||||
|
int len = out.length; |
||||||
|
while (len > 0) { |
||||||
|
int count = readBuf(out, off, len); |
||||||
|
if (count == -1) |
||||||
|
throw new EOFException(); |
||||||
|
off += count; |
||||||
|
len -= count; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private final int readLeByte() throws IOException { |
||||||
|
if (avail <= 0) { |
||||||
|
fillBuf(); |
||||||
|
if (avail <= 0) |
||||||
|
throw new ZipException("EOF in header"); |
||||||
|
} |
||||||
|
return buf[len - avail--] & 0xff; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read an unsigned short in little endian byte order. |
||||||
|
*/ |
||||||
|
private final int readLeShort() throws IOException { |
||||||
|
return readLeByte() | (readLeByte() << 8); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Read an int in little endian byte order. |
||||||
|
*/ |
||||||
|
private final int readLeInt() throws IOException { |
||||||
|
return readLeShort() | (readLeShort() << 16); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open the next entry from the zip archive, and return its description. |
||||||
|
* If the previous entry wasn't closed, this method will close it. |
||||||
|
*/ |
||||||
|
public ZipEntry getNextEntry() throws IOException { |
||||||
|
if (crc == null) |
||||||
|
throw new IOException("Stream closed."); |
||||||
|
if (entry != null) |
||||||
|
closeEntry(); |
||||||
|
|
||||||
|
int header = readLeInt(); |
||||||
|
if (header == CENSIG) { |
||||||
|
/* Central Header reached. */ |
||||||
|
close(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
if (header != LOCSIG) |
||||||
|
throw new ZipException("Wrong Local header signature: " |
||||||
|
+ Integer.toHexString(header)); |
||||||
|
/* skip version */ |
||||||
|
readLeShort(); |
||||||
|
flags = readLeShort(); |
||||||
|
method = readLeShort(); |
||||||
|
int dostime = readLeInt(); |
||||||
|
int crc = readLeInt(); |
||||||
|
csize = readLeInt(); |
||||||
|
size = readLeInt(); |
||||||
|
int nameLen = readLeShort(); |
||||||
|
int extraLen = readLeShort(); |
||||||
|
|
||||||
|
if (method == ZipOutputStream.STORED && csize != size) |
||||||
|
throw new ZipException("Stored, but compressed != uncompressed"); |
||||||
|
|
||||||
|
|
||||||
|
byte[] buffer = new byte[nameLen]; |
||||||
|
readFully(buffer); |
||||||
|
String name = new String(buffer); |
||||||
|
|
||||||
|
entry = createZipEntry(name); |
||||||
|
entryAtEOF = false; |
||||||
|
entry.setMethod(method); |
||||||
|
if ((flags & 8) == 0) { |
||||||
|
entry.setCrc(crc & 0xffffffffL); |
||||||
|
entry.setSize(size & 0xffffffffL); |
||||||
|
entry.setCompressedSize(csize & 0xffffffffL); |
||||||
|
} |
||||||
|
entry.setDOSTime(dostime); |
||||||
|
if (extraLen > 0) { |
||||||
|
byte[] extra = new byte[extraLen]; |
||||||
|
readFully(extra); |
||||||
|
entry.setExtra(extra); |
||||||
|
} |
||||||
|
|
||||||
|
if (method == ZipOutputStream.DEFLATED && avail > 0) { |
||||||
|
System.arraycopy(buf, len - avail, buf, 0, avail); |
||||||
|
len = avail; |
||||||
|
avail = 0; |
||||||
|
inf.setInput(buf, 0, len); |
||||||
|
} |
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
private void readDataDescr() throws IOException { |
||||||
|
if (readLeInt() != EXTSIG) |
||||||
|
throw new ZipException("Data descriptor signature not found"); |
||||||
|
entry.setCrc(readLeInt() & 0xffffffffL); |
||||||
|
csize = readLeInt(); |
||||||
|
size = readLeInt(); |
||||||
|
entry.setSize(size & 0xffffffffL); |
||||||
|
entry.setCompressedSize(csize & 0xffffffffL); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the current zip entry and moves to the next one. |
||||||
|
*/ |
||||||
|
public void closeEntry() throws IOException { |
||||||
|
if (crc == null) |
||||||
|
throw new IOException("Stream closed."); |
||||||
|
if (entry == null) |
||||||
|
return; |
||||||
|
|
||||||
|
if (method == ZipOutputStream.DEFLATED) { |
||||||
|
if ((flags & 8) != 0) { |
||||||
|
/* We don't know how much we must skip, read until end. */ |
||||||
|
byte[] tmp = new byte[2048]; |
||||||
|
while (read(tmp) > 0) |
||||||
|
; |
||||||
|
/* read will close this entry */ |
||||||
|
return; |
||||||
|
} |
||||||
|
csize -= inf.getTotalIn(); |
||||||
|
avail = inf.getRemaining(); |
||||||
|
} |
||||||
|
|
||||||
|
if (avail > csize && csize >= 0) |
||||||
|
avail -= csize; |
||||||
|
else { |
||||||
|
csize -= avail; |
||||||
|
avail = 0; |
||||||
|
while (csize != 0) { |
||||||
|
long skipped = in.skip(csize & 0xffffffffL); |
||||||
|
if (skipped <= 0) |
||||||
|
throw new ZipException("zip archive ends early."); |
||||||
|
csize -= skipped; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size = 0; |
||||||
|
crc.reset(); |
||||||
|
if (method == ZipOutputStream.DEFLATED) |
||||||
|
inf.reset(); |
||||||
|
entry = null; |
||||||
|
entryAtEOF = true; |
||||||
|
} |
||||||
|
|
||||||
|
public int available() throws IOException { |
||||||
|
return entryAtEOF ? 0 : 1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads a byte from the current zip entry. |
||||||
|
* |
||||||
|
* @return the byte or -1 on EOF. |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws ZipException if the deflated stream is corrupted. |
||||||
|
*/ |
||||||
|
public int read() throws IOException { |
||||||
|
byte[] b = new byte[1]; |
||||||
|
if (read(b, 0, 1) <= 0) |
||||||
|
return -1; |
||||||
|
return b[0] & 0xff; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads a block of bytes from the current zip entry. |
||||||
|
* |
||||||
|
* @return the number of bytes read (may be smaller, even before |
||||||
|
* EOF), or -1 on EOF. |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
* @throws ZipException if the deflated stream is corrupted. |
||||||
|
*/ |
||||||
|
public int read(byte[] b, int off, int len) throws IOException { |
||||||
|
if (len == 0) |
||||||
|
return 0; |
||||||
|
if (crc == null) |
||||||
|
throw new IOException("Stream closed."); |
||||||
|
if (entry == null) |
||||||
|
return -1; |
||||||
|
boolean finished = false; |
||||||
|
switch (method) { |
||||||
|
case ZipOutputStream.DEFLATED: |
||||||
|
len = super.read(b, off, len); |
||||||
|
if (len < 0) { |
||||||
|
if (!inf.finished()) |
||||||
|
throw new ZipException("Inflater not finished!?"); |
||||||
|
avail = inf.getRemaining(); |
||||||
|
if ((flags & 8) != 0) |
||||||
|
readDataDescr(); |
||||||
|
|
||||||
|
if (inf.getTotalIn() != csize |
||||||
|
|| inf.getTotalOut() != size) |
||||||
|
throw new ZipException("size mismatch: " + csize + ";" + size + " <-> " + inf.getTotalIn() + ";" + inf.getTotalOut()); |
||||||
|
inf.reset(); |
||||||
|
finished = true; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case ZipOutputStream.STORED: |
||||||
|
|
||||||
|
if (len > csize && csize >= 0) |
||||||
|
len = csize; |
||||||
|
|
||||||
|
len = readBuf(b, off, len); |
||||||
|
if (len > 0) { |
||||||
|
csize -= len; |
||||||
|
size -= len; |
||||||
|
} |
||||||
|
|
||||||
|
if (csize == 0) |
||||||
|
finished = true; |
||||||
|
else if (len < 0) |
||||||
|
throw new ZipException("EOF in stored block"); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (len > 0) |
||||||
|
crc.update(b, off, len); |
||||||
|
|
||||||
|
if (finished) { |
||||||
|
if ((crc.getValue() & 0xffffffffL) != entry.getCrc()) |
||||||
|
throw new ZipException("CRC mismatch"); |
||||||
|
crc.reset(); |
||||||
|
entry = null; |
||||||
|
entryAtEOF = true; |
||||||
|
} |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the zip file. |
||||||
|
* |
||||||
|
* @throws IOException if a i/o error occured. |
||||||
|
*/ |
||||||
|
public void close() throws IOException { |
||||||
|
super.close(); |
||||||
|
crc = null; |
||||||
|
entry = null; |
||||||
|
entryAtEOF = true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new zip entry for the given name. This is equivalent |
||||||
|
* to new ZipEntry(name). |
||||||
|
* |
||||||
|
* @param name the name of the zip entry. |
||||||
|
*/ |
||||||
|
protected ZipEntry createZipEntry(String name) { |
||||||
|
return new ZipEntry(name); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,383 @@ |
|||||||
|
/* me.ag2s.epublib.zip.ZipOutputStream |
||||||
|
Copyright (C) 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 me.ag2s.epublib.zip; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is a FilterOutputStream that writes the files into a zip |
||||||
|
* archive one after another. It has a special method to start a new |
||||||
|
* zip entry. The zip entries contains information about the file name |
||||||
|
* size, compressed size, CRC, etc. |
||||||
|
* <p> |
||||||
|
* It includes support for STORED and DEFLATED entries. |
||||||
|
* <p> |
||||||
|
* This class is not thread safe. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { |
||||||
|
private ArrayList<ZipEntry> entries = new ArrayList<>(); |
||||||
|
private final CRC32 crc = new CRC32(); |
||||||
|
private ZipEntry curEntry = null; |
||||||
|
|
||||||
|
private int curMethod; |
||||||
|
private int size; |
||||||
|
private int offset = 0; |
||||||
|
|
||||||
|
private byte[] zipComment = new byte[0]; |
||||||
|
private int defaultMethod = DEFLATED; |
||||||
|
|
||||||
|
/** |
||||||
|
* Our Zip version is hard coded to 1.0 resp. 2.0 |
||||||
|
*/ |
||||||
|
private final static int ZIP_STORED_VERSION = 10; |
||||||
|
private final static int ZIP_DEFLATED_VERSION = 20; |
||||||
|
|
||||||
|
/** |
||||||
|
* Compression method. This method doesn't compress at all. |
||||||
|
*/ |
||||||
|
public final static int STORED = 0; |
||||||
|
/** |
||||||
|
* Compression method. This method uses the Deflater. |
||||||
|
*/ |
||||||
|
public final static int DEFLATED = 8; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Zip output stream, writing a zip archive. |
||||||
|
* |
||||||
|
* @param out the output stream to which the zip archive is written. |
||||||
|
*/ |
||||||
|
public ZipOutputStream(OutputStream out) { |
||||||
|
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the zip file comment. |
||||||
|
* |
||||||
|
* @param comment the comment. |
||||||
|
* @throws IllegalArgumentException if encoding of comment is |
||||||
|
* longer than 0xffff bytes. |
||||||
|
*/ |
||||||
|
public void setComment(String comment) { |
||||||
|
byte[] commentBytes; |
||||||
|
commentBytes = comment.getBytes(); |
||||||
|
if (commentBytes.length > 0xffff) |
||||||
|
throw new IllegalArgumentException("Comment too long."); |
||||||
|
zipComment = commentBytes; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets default compression method. If the Zip entry specifies |
||||||
|
* another method its method takes precedence. |
||||||
|
* |
||||||
|
* @param method the method. |
||||||
|
* @throws IllegalArgumentException if method is not supported. |
||||||
|
* @see #STORED |
||||||
|
* @see #DEFLATED |
||||||
|
*/ |
||||||
|
public void setMethod(int method) { |
||||||
|
if (method != STORED && method != DEFLATED) |
||||||
|
throw new IllegalArgumentException("Method not supported."); |
||||||
|
defaultMethod = method; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets default compression level. The new level will be activated |
||||||
|
* immediately. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if level is not supported. |
||||||
|
* @see Deflater |
||||||
|
*/ |
||||||
|
public void setLevel(int level) { |
||||||
|
def.setLevel(level); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write an unsigned short in little endian byte order. |
||||||
|
*/ |
||||||
|
private final void writeLeShort(int value) throws IOException { |
||||||
|
out.write(value & 0xff); |
||||||
|
out.write((value >> 8) & 0xff); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Write an int in little endian byte order. |
||||||
|
*/ |
||||||
|
private final void writeLeInt(int value) throws IOException { |
||||||
|
writeLeShort(value); |
||||||
|
writeLeShort(value >> 16); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Starts a new Zip entry. It automatically closes the previous |
||||||
|
* entry if present. If the compression method is stored, the entry |
||||||
|
* must have a valid size and crc, otherwise all elements (except |
||||||
|
* name) are optional, but must be correct if present. If the time |
||||||
|
* is not set in the entry, the current time is used. |
||||||
|
* |
||||||
|
* @param entry the entry. |
||||||
|
* @throws IOException if an I/O error occured. |
||||||
|
* @throws ZipException if stream was finished. |
||||||
|
*/ |
||||||
|
public void putNextEntry(ZipEntry entry) throws IOException { |
||||||
|
if (entries == null) |
||||||
|
throw new ZipException("ZipOutputStream was finished"); |
||||||
|
|
||||||
|
int method = entry.getMethod(); |
||||||
|
int flags = 0; |
||||||
|
if (method == -1) |
||||||
|
method = defaultMethod; |
||||||
|
|
||||||
|
if (method == STORED) { |
||||||
|
if (entry.getCompressedSize() >= 0) { |
||||||
|
if (entry.getSize() < 0) |
||||||
|
entry.setSize(entry.getCompressedSize()); |
||||||
|
else if (entry.getSize() != entry.getCompressedSize()) |
||||||
|
throw new ZipException |
||||||
|
("Method STORED, but compressed size != size"); |
||||||
|
} else |
||||||
|
entry.setCompressedSize(entry.getSize()); |
||||||
|
|
||||||
|
if (entry.getSize() < 0) |
||||||
|
throw new ZipException("Method STORED, but size not set"); |
||||||
|
if (entry.getCrc() < 0) |
||||||
|
throw new ZipException("Method STORED, but crc not set"); |
||||||
|
} else if (method == DEFLATED) { |
||||||
|
if (entry.getCompressedSize() < 0 |
||||||
|
|| entry.getSize() < 0 || entry.getCrc() < 0) |
||||||
|
flags |= 8; |
||||||
|
} |
||||||
|
|
||||||
|
if (curEntry != null) |
||||||
|
closeEntry(); |
||||||
|
|
||||||
|
if (entry.getTime() < 0) |
||||||
|
entry.setTime(System.currentTimeMillis()); |
||||||
|
|
||||||
|
entry.flags = flags; |
||||||
|
entry.offset = offset; |
||||||
|
entry.setMethod(method); |
||||||
|
curMethod = method; |
||||||
|
/* Write the local file header */ |
||||||
|
writeLeInt(LOCSIG); |
||||||
|
writeLeShort(method == STORED |
||||||
|
? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); |
||||||
|
writeLeShort(flags); |
||||||
|
writeLeShort(method); |
||||||
|
writeLeInt(entry.getDOSTime()); |
||||||
|
if ((flags & 8) == 0) { |
||||||
|
writeLeInt((int) entry.getCrc()); |
||||||
|
writeLeInt((int) entry.getCompressedSize()); |
||||||
|
writeLeInt((int) entry.getSize()); |
||||||
|
} else { |
||||||
|
writeLeInt(0); |
||||||
|
writeLeInt(0); |
||||||
|
writeLeInt(0); |
||||||
|
} |
||||||
|
byte[] name = entry.getName().getBytes(); |
||||||
|
if (name.length > 0xffff) |
||||||
|
throw new ZipException("Name too long."); |
||||||
|
byte[] extra = entry.getExtra(); |
||||||
|
if (extra == null) |
||||||
|
extra = new byte[0]; |
||||||
|
writeLeShort(name.length); |
||||||
|
writeLeShort(extra.length); |
||||||
|
out.write(name); |
||||||
|
out.write(extra); |
||||||
|
|
||||||
|
offset += LOCHDR + name.length + extra.length; |
||||||
|
|
||||||
|
/* Activate the entry. */ |
||||||
|
|
||||||
|
curEntry = entry; |
||||||
|
crc.reset(); |
||||||
|
if (method == DEFLATED) |
||||||
|
def.reset(); |
||||||
|
size = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Closes the current entry. |
||||||
|
* |
||||||
|
* @throws IOException if an I/O error occured. |
||||||
|
* @throws ZipException if no entry is active. |
||||||
|
*/ |
||||||
|
public void closeEntry() throws IOException { |
||||||
|
if (curEntry == null) |
||||||
|
throw new ZipException("No open entry"); |
||||||
|
|
||||||
|
/* First finish the deflater, if appropriate */ |
||||||
|
if (curMethod == DEFLATED) |
||||||
|
super.finish(); |
||||||
|
|
||||||
|
int csize = curMethod == DEFLATED ? def.getTotalOut() : size; |
||||||
|
|
||||||
|
if (curEntry.getSize() < 0) |
||||||
|
curEntry.setSize(size); |
||||||
|
else if (curEntry.getSize() != size) |
||||||
|
throw new ZipException("size was " + size |
||||||
|
+ ", but I expected " + curEntry.getSize()); |
||||||
|
|
||||||
|
if (curEntry.getCompressedSize() < 0) |
||||||
|
curEntry.setCompressedSize(csize); |
||||||
|
else if (curEntry.getCompressedSize() != csize) |
||||||
|
throw new ZipException("compressed size was " + csize |
||||||
|
+ ", but I expected " + curEntry.getSize()); |
||||||
|
|
||||||
|
if (curEntry.getCrc() < 0) |
||||||
|
curEntry.setCrc(crc.getValue()); |
||||||
|
else if (curEntry.getCrc() != crc.getValue()) |
||||||
|
throw new ZipException("crc was " + Long.toHexString(crc.getValue()) |
||||||
|
+ ", but I expected " |
||||||
|
+ Long.toHexString(curEntry.getCrc())); |
||||||
|
|
||||||
|
offset += csize; |
||||||
|
|
||||||
|
/* Now write the data descriptor entry if needed. */ |
||||||
|
if (curMethod == DEFLATED && (curEntry.flags & 8) != 0) { |
||||||
|
writeLeInt(EXTSIG); |
||||||
|
writeLeInt((int) curEntry.getCrc()); |
||||||
|
writeLeInt((int) curEntry.getCompressedSize()); |
||||||
|
writeLeInt((int) curEntry.getSize()); |
||||||
|
offset += EXTHDR; |
||||||
|
} |
||||||
|
|
||||||
|
entries.add(curEntry); |
||||||
|
//entries.addElement(curEntry);
|
||||||
|
curEntry = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes the given buffer to the current entry. |
||||||
|
* |
||||||
|
* @throws IOException if an I/O error occured. |
||||||
|
* @throws ZipException if no entry is active. |
||||||
|
*/ |
||||||
|
public void write(byte[] b, int off, int len) throws IOException { |
||||||
|
if (curEntry == null) |
||||||
|
throw new ZipException("No open entry."); |
||||||
|
|
||||||
|
switch (curMethod) { |
||||||
|
case DEFLATED: |
||||||
|
super.write(b, off, len); |
||||||
|
break; |
||||||
|
|
||||||
|
case STORED: |
||||||
|
out.write(b, off, len); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
crc.update(b, off, len); |
||||||
|
size += len; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finishes the stream. This will write the central directory at the |
||||||
|
* end of the zip file and flush the stream. |
||||||
|
* |
||||||
|
* @throws IOException if an I/O error occured. |
||||||
|
*/ |
||||||
|
public void finish() throws IOException { |
||||||
|
if (entries == null) |
||||||
|
return; |
||||||
|
if (curEntry != null) |
||||||
|
closeEntry(); |
||||||
|
|
||||||
|
int numEntries = 0; |
||||||
|
int sizeEntries = 0; |
||||||
|
|
||||||
|
//Enumeration<ZipEntry> items = entries.elements();
|
||||||
|
//items.nextElement();
|
||||||
|
for (ZipEntry entry : entries) { |
||||||
|
int method = entry.getMethod(); |
||||||
|
writeLeInt(CENSIG); |
||||||
|
writeLeShort(method == STORED |
||||||
|
? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); |
||||||
|
writeLeShort(method == STORED |
||||||
|
? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); |
||||||
|
writeLeShort(entry.flags); |
||||||
|
writeLeShort(method); |
||||||
|
writeLeInt(entry.getDOSTime()); |
||||||
|
writeLeInt((int) entry.getCrc()); |
||||||
|
writeLeInt((int) entry.getCompressedSize()); |
||||||
|
writeLeInt((int) entry.getSize()); |
||||||
|
|
||||||
|
byte[] name = entry.getName().getBytes(); |
||||||
|
if (name.length > 0xffff) |
||||||
|
throw new ZipException("Name too long."); |
||||||
|
byte[] extra = entry.getExtra(); |
||||||
|
if (extra == null) |
||||||
|
extra = new byte[0]; |
||||||
|
String strComment = entry.getComment(); |
||||||
|
byte[] comment = strComment != null |
||||||
|
? strComment.getBytes() : new byte[0]; |
||||||
|
if (comment.length > 0xffff) |
||||||
|
throw new ZipException("Comment too long."); |
||||||
|
|
||||||
|
writeLeShort(name.length); |
||||||
|
writeLeShort(extra.length); |
||||||
|
writeLeShort(comment.length); |
||||||
|
writeLeShort(0); /* disk number */ |
||||||
|
writeLeShort(0); /* internal file attr */ |
||||||
|
writeLeInt(0); /* external file attr */ |
||||||
|
writeLeInt(entry.offset); |
||||||
|
|
||||||
|
out.write(name); |
||||||
|
out.write(extra); |
||||||
|
out.write(comment); |
||||||
|
numEntries++; |
||||||
|
sizeEntries += CENHDR + name.length + extra.length + comment.length; |
||||||
|
} |
||||||
|
|
||||||
|
writeLeInt(ENDSIG); |
||||||
|
writeLeShort(0); /* disk number */ |
||||||
|
writeLeShort(0); /* disk with start of central dir */ |
||||||
|
writeLeShort(numEntries); |
||||||
|
writeLeShort(numEntries); |
||||||
|
writeLeInt(sizeEntries); |
||||||
|
writeLeInt(offset); |
||||||
|
writeLeShort(zipComment.length); |
||||||
|
out.write(zipComment); |
||||||
|
out.flush(); |
||||||
|
entries = null; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue