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