parent
38929910f0
commit
90b6e31ff1
@ -1,125 +0,0 @@ |
|||||||
<component name="ProjectCodeStyleConfiguration"> |
|
||||||
<code_scheme name="Project" version="173"> |
|
||||||
<JetCodeStyleSettings> |
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> |
|
||||||
</JetCodeStyleSettings> |
|
||||||
<codeStyleSettings language="XML"> |
|
||||||
<indentOptions> |
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" /> |
|
||||||
</indentOptions> |
|
||||||
<arrangement> |
|
||||||
<rules> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>xmlns:android</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>xmlns:.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>BY_NAME</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*:id</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*:name</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>name</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>style</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>BY_NAME</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>ANDROID_ATTRIBUTE_ORDER</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>.*</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>BY_NAME</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
</rules> |
|
||||||
</arrangement> |
|
||||||
</codeStyleSettings> |
|
||||||
<codeStyleSettings language="kotlin"> |
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> |
|
||||||
<indentOptions> |
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" /> |
|
||||||
</indentOptions> |
|
||||||
</codeStyleSettings> |
|
||||||
</code_scheme> |
|
||||||
</component> |
|
@ -1,197 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,731 +0,0 @@ |
|||||||
package me.ag2s.epublib.zip; |
|
||||||
|
|
||||||
import static me.ag2s.utils.ThrowableUtils.rethrowAsIOException; |
|
||||||
|
|
||||||
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.io.UTFDataFormatException; |
|
||||||
import java.nio.ByteBuffer; |
|
||||||
import java.nio.ByteOrder; |
|
||||||
import java.nio.channels.FileChannel; |
|
||||||
|
|
||||||
import me.ag2s.utils.AndroidCloseGuard; |
|
||||||
|
|
||||||
@SuppressWarnings("unused") |
|
||||||
public class AndroidRandomReadableFile implements DataInput, Closeable { |
|
||||||
private final ParcelFileDescriptor pfd; |
|
||||||
private FileInputStream fis; |
|
||||||
private final Object lock = new Object(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 读取基本类型的buffer |
|
||||||
*/ |
|
||||||
private final byte[] readBuffer = new byte[8]; |
|
||||||
private long pos = 0; |
|
||||||
|
|
||||||
private final AndroidCloseGuard guard = AndroidCloseGuard.getInstance(); |
|
||||||
|
|
||||||
public AndroidRandomReadableFile(@NonNull Context context, @NonNull Uri treeUri) throws FileNotFoundException { |
|
||||||
pfd = context.getContentResolver().openFileDescriptor(treeUri, "r"); |
|
||||||
fis = new FileInputStream(pfd.getFileDescriptor()); |
|
||||||
guard.open("close"); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
public final FileDescriptor getFD() { |
|
||||||
return pfd.getFileDescriptor(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the unique {@link java.nio.channels.FileChannel FileChannel} |
|
||||||
* object associated with this file. |
|
||||||
* |
|
||||||
* <p> The {@link java.nio.channels.FileChannel#position() |
|
||||||
* position} of the returned channel will always be equal to |
|
||||||
* this object's file-pointer offset as returned by the {@link |
|
||||||
* #getFilePointer getFilePointer} method. Changing this object's |
|
||||||
* file-pointer offset, whether explicitly or by reading or writing bytes, |
|
||||||
* will change the position of the channel, and vice versa. Changing the |
|
||||||
* file's length via this object will change the length seen via the file |
|
||||||
* channel, and vice versa. |
|
||||||
* |
|
||||||
* @return the file channel associated with this file |
|
||||||
* @spec JSR-51 |
|
||||||
* @since 1.4 |
|
||||||
*/ |
|
||||||
public final FileChannel getChannel() { |
|
||||||
synchronized (lock) { |
|
||||||
if (fis == null || pos != getPos()) { |
|
||||||
fis = new FileInputStream(pfd.getFileDescriptor()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return fis.getChannel(); |
|
||||||
} |
|
||||||
|
|
||||||
public final FileInputStream getFileInputStream() { |
|
||||||
synchronized (lock) { |
|
||||||
if (fis == null || this.pos != getPos()) { |
|
||||||
this.pos = getPos(); |
|
||||||
fis = new FileInputStream(pfd.getFileDescriptor()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return fis; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads a byte of data from this input stream. This method blocks |
|
||||||
* if no input is yet available. |
|
||||||
* |
|
||||||
* @return the next byte of data, or <code>-1</code> if the end of the |
|
||||||
* file is reached. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
public int read() throws IOException { |
|
||||||
return (read(readBuffer, 0, 1) != -1) ? readBuffer[0] & 0xff : -1; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads a sub array as a sequence of bytes. |
|
||||||
* |
|
||||||
* @param b the buffer into which the data is read. |
|
||||||
* @param off the start offset of the data. |
|
||||||
* @param len the number of bytes to read. |
|
||||||
* @throws IOException If an I/O error has occurred. |
|
||||||
*/ |
|
||||||
private int readBytes(byte[] b, int off, int len) throws IOException { |
|
||||||
try { |
|
||||||
return android.system.Os.read(pfd.getFileDescriptor(), b, off, len); |
|
||||||
} catch (ErrnoException e) { |
|
||||||
throw rethrowAsIOException(e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Reads up to <code>b.length</code> bytes of data from this input |
|
||||||
* stream into an array of bytes. This method blocks until some input |
|
||||||
* is available. |
|
||||||
* |
|
||||||
* @param b the buffer into which the data is read. |
|
||||||
* @return the total number of bytes read into the buffer, or |
|
||||||
* <code>-1</code> if there is no more data because the end of |
|
||||||
* the file has been reached. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
public int read(byte[] b) throws IOException { |
|
||||||
return readBytes(b, 0, b.length); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads up to <code>len</code> bytes of data from this input stream |
|
||||||
* into an array of bytes. If <code>len</code> is not zero, the method |
|
||||||
* blocks until some input is available; otherwise, no |
|
||||||
* bytes are read and <code>0</code> is returned. |
|
||||||
* |
|
||||||
* @param b the buffer into which the data is read. |
|
||||||
* @param off the start offset in the destination array <code>b</code> |
|
||||||
* @param len the maximum number of bytes read. |
|
||||||
* @return the total number of bytes read into the buffer, or |
|
||||||
* <code>-1</code> if there is no more data because the end of |
|
||||||
* the file has been reached. |
|
||||||
* @throws NullPointerException If <code>b</code> is <code>null</code>. |
|
||||||
* @throws IndexOutOfBoundsException If <code>off</code> is negative, |
|
||||||
* <code>len</code> is negative, or <code>len</code> is greater than |
|
||||||
* <code>b.length - off</code> |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
public int read(byte[] b, int off, int len) throws IOException { |
|
||||||
return readBytes(b, off, len); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
private void syncInputStream() { |
|
||||||
this.pos = getPos(); |
|
||||||
fis = new FileInputStream(pfd.getFileDescriptor()); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
public void seek(long pos) throws IOException { |
|
||||||
try { |
|
||||||
android.system.Os.lseek(pfd.getFileDescriptor(), pos, OsConstants.SEEK_SET); |
|
||||||
} catch (ErrnoException e) { |
|
||||||
throw rethrowAsIOException(e); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public long length() throws IOException { |
|
||||||
try { |
|
||||||
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_END); |
|
||||||
} catch (ErrnoException e) { |
|
||||||
throw rethrowAsIOException(e); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the current offset in this file. |
|
||||||
* |
|
||||||
* @return the offset from the beginning of the file, in bytes, |
|
||||||
* at which the next read or write occurs. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
public long getFilePointer() throws IOException { |
|
||||||
try { |
|
||||||
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR); |
|
||||||
} catch (ErrnoException e) { |
|
||||||
throw rethrowAsIOException(e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public long getPos() { |
|
||||||
try { |
|
||||||
return android.system.Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR); |
|
||||||
} catch (ErrnoException e) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Reads some bytes from an input |
|
||||||
* stream and stores them into the buffer |
|
||||||
* array {@code b}. The number of bytes |
|
||||||
* read is equal |
|
||||||
* to the length of {@code b}. |
|
||||||
* <p> |
|
||||||
* This method blocks until one of the |
|
||||||
* following conditions occurs: |
|
||||||
* <ul> |
|
||||||
* <li>{@code b.length} |
|
||||||
* bytes of input data are available, in which |
|
||||||
* case a normal return is made. |
|
||||||
* |
|
||||||
* <li>End of |
|
||||||
* file is detected, in which case an {@code EOFException} |
|
||||||
* is thrown. |
|
||||||
* |
|
||||||
* <li>An I/O error occurs, in |
|
||||||
* which case an {@code IOException} other |
|
||||||
* than {@code EOFException} is thrown. |
|
||||||
* </ul> |
|
||||||
* <p> |
|
||||||
* If {@code b} is {@code null}, |
|
||||||
* a {@code NullPointerException} is thrown. |
|
||||||
* If {@code b.length} is zero, then |
|
||||||
* no bytes are read. Otherwise, the first |
|
||||||
* byte read is stored into element {@code b[0]}, |
|
||||||
* the next one into {@code b[1]}, and |
|
||||||
* so on. |
|
||||||
* If an exception is thrown from |
|
||||||
* this method, then it may be that some but |
|
||||||
* not all bytes of {@code b} have been |
|
||||||
* updated with data from the input stream. |
|
||||||
* |
|
||||||
* @param b the buffer into which the data is read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public void readFully(byte[] b) throws IOException { |
|
||||||
readFully(b, 0, b.length); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads {@code len} |
|
||||||
* bytes from |
|
||||||
* an input stream. |
|
||||||
* <p> |
|
||||||
* This method |
|
||||||
* blocks until one of the following conditions |
|
||||||
* occurs: |
|
||||||
* <ul> |
|
||||||
* <li>{@code len} bytes |
|
||||||
* of input data are available, in which case |
|
||||||
* a normal return is made. |
|
||||||
* |
|
||||||
* <li>End of file |
|
||||||
* is detected, in which case an {@code EOFException} |
|
||||||
* is thrown. |
|
||||||
* |
|
||||||
* <li>An I/O error occurs, in |
|
||||||
* which case an {@code IOException} other |
|
||||||
* than {@code EOFException} is thrown. |
|
||||||
* </ul> |
|
||||||
* <p> |
|
||||||
* If {@code b} is {@code null}, |
|
||||||
* a {@code NullPointerException} is thrown. |
|
||||||
* If {@code off} is negative, or {@code len} |
|
||||||
* is negative, or {@code off+len} is |
|
||||||
* greater than the length of the array {@code b}, |
|
||||||
* then an {@code IndexOutOfBoundsException} |
|
||||||
* is thrown. |
|
||||||
* If {@code len} is zero, |
|
||||||
* then no bytes are read. Otherwise, the first |
|
||||||
* byte read is stored into element {@code b[off]}, |
|
||||||
* the next one into {@code b[off+1]}, |
|
||||||
* and so on. The number of bytes read is, |
|
||||||
* at most, equal to {@code len}. |
|
||||||
* |
|
||||||
* @param b the buffer into which the data is read. |
|
||||||
* @param off an int specifying the offset into the data. |
|
||||||
* @param len an int specifying the number of bytes to read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public void readFully(byte[] b, int off, int len) throws IOException { |
|
||||||
int n = 0; |
|
||||||
do { |
|
||||||
int count = this.read(b, off + n, len - n); |
|
||||||
if (count < 0) |
|
||||||
throw new EOFException(); |
|
||||||
n += count; |
|
||||||
} while (n < len); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Makes an attempt to skip over |
|
||||||
* {@code n} bytes |
|
||||||
* of data from the input |
|
||||||
* stream, discarding the skipped bytes. However, |
|
||||||
* it may skip |
|
||||||
* over some smaller number of |
|
||||||
* bytes, possibly zero. This may result from |
|
||||||
* any of a |
|
||||||
* number of conditions; reaching |
|
||||||
* end of file before {@code n} bytes |
|
||||||
* have been skipped is |
|
||||||
* only one possibility. |
|
||||||
* This method never throws an {@code EOFException}. |
|
||||||
* The actual |
|
||||||
* number of bytes skipped is returned. |
|
||||||
* |
|
||||||
* @param n the number of bytes to be skipped. |
|
||||||
* @return the number of bytes actually skipped. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int skipBytes(int n) throws IOException { |
|
||||||
long pos; |
|
||||||
long len; |
|
||||||
long newpos; |
|
||||||
|
|
||||||
if (n <= 0) { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
pos = getFilePointer(); |
|
||||||
len = length(); |
|
||||||
newpos = pos + n; |
|
||||||
if (newpos > len) { |
|
||||||
newpos = len; |
|
||||||
} |
|
||||||
seek(newpos); |
|
||||||
|
|
||||||
/* return the actual number of bytes skipped */ |
|
||||||
return (int) (newpos - pos); |
|
||||||
// try {
|
|
||||||
// byte[] b = new byte[n];
|
|
||||||
// return android.system.Os.read(pfd.getFileDescriptor(), b, 0, b.length);
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// return -1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Reads one input byte and returns |
|
||||||
* {@code true} if that byte is nonzero, |
|
||||||
* {@code false} if that byte is zero. |
|
||||||
* This method is suitable for reading |
|
||||||
* the byte written by the {@code writeBoolean} |
|
||||||
* method of interface {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the {@code boolean} value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public boolean readBoolean() throws IOException { |
|
||||||
int ch = this.read(); |
|
||||||
if (ch < 0) |
|
||||||
throw new EOFException(); |
|
||||||
return (ch != 0); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns one input byte. |
|
||||||
* The byte is treated as a signed value in |
|
||||||
* the range {@code -128} through {@code 127}, |
|
||||||
* inclusive. |
|
||||||
* This method is suitable for |
|
||||||
* reading the byte written by the {@code writeByte} |
|
||||||
* method of interface {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the 8-bit value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public byte readByte() throws IOException { |
|
||||||
int ch = this.read(); |
|
||||||
if (ch < 0) |
|
||||||
throw new EOFException(); |
|
||||||
return (byte) (ch); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Reads one input byte, zero-extends |
|
||||||
* it to type {@code int}, and returns |
|
||||||
* the result, which is therefore in the range |
|
||||||
* {@code 0} |
|
||||||
* through {@code 255}. |
|
||||||
* This method is suitable for reading |
|
||||||
* the byte written by the {@code writeByte} |
|
||||||
* method of interface {@code DataOutput} |
|
||||||
* if the argument to {@code writeByte} |
|
||||||
* was intended to be a value in the range |
|
||||||
* {@code 0} through {@code 255}. |
|
||||||
* |
|
||||||
* @return the unsigned 8-bit value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int readUnsignedByte() throws IOException { |
|
||||||
int ch = this.read(); |
|
||||||
if (ch < 0) |
|
||||||
throw new EOFException(); |
|
||||||
return ch; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Reads two input bytes and returns |
|
||||||
* a {@code short} value. Let {@code a} |
|
||||||
* be the first byte read and {@code b} |
|
||||||
* be the second byte. The value |
|
||||||
* returned |
|
||||||
* is: |
|
||||||
* <pre>{@code (short)((a << 8) | (b & 0xff)) |
|
||||||
* }</pre> |
|
||||||
* This method |
|
||||||
* is suitable for reading the bytes written |
|
||||||
* by the {@code writeShort} method of |
|
||||||
* interface {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the 16-bit value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public short readShort() throws IOException { |
|
||||||
readFully(readBuffer, 0, 2); |
|
||||||
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads two input bytes and returns |
|
||||||
* an {@code int} value in the range {@code 0} |
|
||||||
* through {@code 65535}. Let {@code a} |
|
||||||
* be the first byte read and |
|
||||||
* {@code b} |
|
||||||
* be the second byte. The value returned is: |
|
||||||
* <pre>{@code (((a & 0xff) << 8) | (b & 0xff)) |
|
||||||
* }</pre> |
|
||||||
* This method is suitable for reading the bytes |
|
||||||
* written by the {@code writeShort} method |
|
||||||
* of interface {@code DataOutput} if |
|
||||||
* the argument to {@code writeShort} |
|
||||||
* was intended to be a value in the range |
|
||||||
* {@code 0} through {@code 65535}. |
|
||||||
* |
|
||||||
* @return the unsigned 16-bit value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int readUnsignedShort() throws IOException { |
|
||||||
readFully(readBuffer, 0, 2); |
|
||||||
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get() & 0xffff; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads two input bytes and returns a {@code char} value. |
|
||||||
* Let {@code a} |
|
||||||
* be the first byte read and {@code b} |
|
||||||
* be the second byte. The value |
|
||||||
* returned is: |
|
||||||
* <pre>{@code (char)((a << 8) | (b & 0xff)) |
|
||||||
* }</pre> |
|
||||||
* This method |
|
||||||
* is suitable for reading bytes written by |
|
||||||
* the {@code writeChar} method of interface
|
|
||||||
* {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the {@code char} value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public char readChar() throws IOException { |
|
||||||
readFully(readBuffer, 0, 2); |
|
||||||
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asCharBuffer().get(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads four input bytes and returns an |
|
||||||
* {@code int} value. Let {@code a-d} |
|
||||||
* be the first through fourth bytes read. The value returned is: |
|
||||||
* <pre>{@code |
|
||||||
* (((a & 0xff) << 24) | ((b & 0xff) << 16) | |
|
||||||
* ((c & 0xff) << 8) | (d & 0xff)) |
|
||||||
* }</pre> |
|
||||||
* This method is suitable |
|
||||||
* for reading bytes written by the {@code writeInt} |
|
||||||
* method of interface {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the {@code int} value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int readInt() throws IOException { |
|
||||||
readFully(readBuffer, 0, 4); |
|
||||||
return ByteBuffer.wrap(readBuffer).order(ByteOrder.BIG_ENDIAN).asIntBuffer().get(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Reads eight input bytes and returns |
|
||||||
* a {@code long} value. Let {@code a-h} |
|
||||||
* be the first through eighth bytes read. |
|
||||||
* The value returned is: |
|
||||||
* <pre>{@code |
|
||||||
* (((long)(a & 0xff) << 56) | |
|
||||||
* ((long)(b & 0xff) << 48) | |
|
||||||
* ((long)(c & 0xff) << 40) | |
|
||||||
* ((long)(d & 0xff) << 32) | |
|
||||||
* ((long)(e & 0xff) << 24) | |
|
||||||
* ((long)(f & 0xff) << 16) | |
|
||||||
* ((long)(g & 0xff) << 8) | |
|
||||||
* ((long)(h & 0xff))) |
|
||||||
* }</pre> |
|
||||||
* <p> |
|
||||||
* This method is suitable |
|
||||||
* for reading bytes written by the {@code writeLong} |
|
||||||
* method of interface {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the {@code long} value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@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)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads four input bytes and returns |
|
||||||
* a {@code float} value. It does this |
|
||||||
* by first constructing an {@code int} |
|
||||||
* value in exactly the manner |
|
||||||
* of the {@code readInt} |
|
||||||
* method, then converting this {@code int} |
|
||||||
* value to a {@code float} in |
|
||||||
* exactly the manner of the method {@code Float.intBitsToFloat}. |
|
||||||
* This method is suitable for reading |
|
||||||
* bytes written by the {@code writeFloat} |
|
||||||
* method of interface {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the {@code float} value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public float readFloat() throws IOException { |
|
||||||
return Float.intBitsToFloat(readInt()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads eight input bytes and returns |
|
||||||
* a {@code double} value. It does this |
|
||||||
* by first constructing a {@code long} |
|
||||||
* value in exactly the manner |
|
||||||
* of the {@code readLong} |
|
||||||
* method, then converting this {@code long} |
|
||||||
* value to a {@code double} in exactly |
|
||||||
* the manner of the method {@code Double.longBitsToDouble}. |
|
||||||
* This method is suitable for reading |
|
||||||
* bytes written by the {@code writeDouble} |
|
||||||
* method of interface {@code DataOutput}. |
|
||||||
* |
|
||||||
* @return the {@code double} value read. |
|
||||||
* @throws EOFException if this stream reaches the end before reading |
|
||||||
* all the bytes. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public double readDouble() throws IOException { |
|
||||||
return Double.longBitsToDouble(readLong()); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Reads the next line of text from this file. This method successively |
|
||||||
* reads bytes from the file, starting at the current file pointer, |
|
||||||
* until it reaches a line terminator or the end |
|
||||||
* of the file. Each byte is converted into a character by taking the |
|
||||||
* byte's value for the lower eight bits of the character and setting the |
|
||||||
* high eight bits of the character to zero. This method does not, |
|
||||||
* therefore, support the full Unicode character set. |
|
||||||
* |
|
||||||
* <p> A line of text is terminated by a carriage-return character |
|
||||||
* ({@code '\u005Cr'}), a newline character ({@code '\u005Cn'}), a |
|
||||||
* carriage-return character immediately followed by a newline character, |
|
||||||
* or the end of the file. Line-terminating characters are discarded and |
|
||||||
* are not included as part of the string returned. |
|
||||||
* |
|
||||||
* <p> This method blocks until a newline character is read, a carriage |
|
||||||
* return and the byte following it are read (to see if it is a newline), |
|
||||||
* the end of the file is reached, or an exception is thrown. |
|
||||||
* |
|
||||||
* @return the next line of text from this file, or null if end |
|
||||||
* of file is encountered before even one byte is read. |
|
||||||
* @throws IOException if an I/O error occurs. |
|
||||||
*/ |
|
||||||
|
|
||||||
@Override |
|
||||||
public String readLine() throws IOException { |
|
||||||
StringBuilder input = new StringBuilder(); |
|
||||||
int c = -1; |
|
||||||
boolean eol = false; |
|
||||||
|
|
||||||
while (!eol) { |
|
||||||
switch (c = read()) { |
|
||||||
case -1: |
|
||||||
case '\n': |
|
||||||
eol = true; |
|
||||||
break; |
|
||||||
case '\r': |
|
||||||
eol = true; |
|
||||||
long cur = getFilePointer(); |
|
||||||
if ((read()) != '\n') { |
|
||||||
seek(cur); |
|
||||||
} |
|
||||||
break; |
|
||||||
default: |
|
||||||
input.append((char) c); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if ((c == -1) && (input.length() == 0)) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
return input.toString(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* See the general contract of the <code>readUTF</code> |
|
||||||
* method of <code>DataInput</code>. |
|
||||||
* <p> |
|
||||||
* Bytes |
|
||||||
* for this operation are read from the contained |
|
||||||
* input stream. |
|
||||||
* |
|
||||||
* @return a Unicode string. |
|
||||||
* @throws EOFException if this input stream reaches the end before |
|
||||||
* reading all the bytes. |
|
||||||
* @throws IOException the stream has been closed and the contained |
|
||||||
* input stream does not support reading after close, or |
|
||||||
* another I/O error occurs. |
|
||||||
* @throws UTFDataFormatException if the bytes do not represent a valid |
|
||||||
* modified UTF-8 encoding of a string. |
|
||||||
* @see java.io.DataInputStream#readUTF(java.io.DataInput) |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public String readUTF() throws IOException { |
|
||||||
return DataInputStream.readUTF(this); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void close() throws IOException { |
|
||||||
|
|
||||||
guard.close(); |
|
||||||
|
|
||||||
try { |
|
||||||
if (fis != null) { |
|
||||||
fis.close(); |
|
||||||
} |
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
try { |
|
||||||
if (pfd != null) { |
|
||||||
pfd.close(); |
|
||||||
} |
|
||||||
|
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
protected void finalize() throws Throwable { |
|
||||||
try { |
|
||||||
// Note that guard could be null if the constructor threw.
|
|
||||||
|
|
||||||
guard.warnIfOpen(); |
|
||||||
|
|
||||||
|
|
||||||
close(); |
|
||||||
|
|
||||||
} finally { |
|
||||||
super.finalize(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,134 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,128 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
@ -1,95 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
@ -1,84 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,512 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,77 +0,0 @@ |
|||||||
/* 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}; |
|
||||||
} |
|
@ -1,653 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,704 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,190 +0,0 @@ |
|||||||
/* 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(); |
|
||||||
} |
|
||||||
} |
|
@ -1,52 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
@ -1,338 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,146 +0,0 @@ |
|||||||
/* 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 )");
|
|
||||||
} |
|
||||||
} |
|
@ -1,681 +0,0 @@ |
|||||||
/* 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(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,187 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,187 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,230 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,161 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,181 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
@ -1,206 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
@ -1,96 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
|
|
@ -1,389 +0,0 @@ |
|||||||
/* 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(); |
|
||||||
} |
|
||||||
} |
|
@ -1,69 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,526 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,344 +0,0 @@ |
|||||||
/* 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); |
|
||||||
} |
|
||||||
} |
|
@ -1,383 +0,0 @@ |
|||||||
/* 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; |
|
||||||
} |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
package me.ag2s.utils; |
|
||||||
|
|
||||||
import android.os.Build; |
|
||||||
import android.util.CloseGuard; |
|
||||||
|
|
||||||
import androidx.annotation.NonNull; |
|
||||||
import androidx.annotation.RequiresApi; |
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.R) |
|
||||||
final class Android11CloseGuard implements AndroidCloseGuard { |
|
||||||
@NonNull |
|
||||||
private final CloseGuard mImpl; |
|
||||||
|
|
||||||
|
|
||||||
public Android11CloseGuard() { |
|
||||||
mImpl = new CloseGuard(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void open(@NonNull String closeMethodName) { |
|
||||||
mImpl.open(closeMethodName); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void close() { |
|
||||||
mImpl.close(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void warnIfOpen() { |
|
||||||
mImpl.warnIfOpen(); |
|
||||||
} |
|
||||||
} |
|
@ -1,38 +0,0 @@ |
|||||||
package me.ag2s.utils; |
|
||||||
|
|
||||||
|
|
||||||
import android.os.Build; |
|
||||||
|
|
||||||
import androidx.annotation.NonNull; |
|
||||||
|
|
||||||
public interface AndroidCloseGuard { |
|
||||||
|
|
||||||
|
|
||||||
static AndroidCloseGuard getInstance() { |
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { |
|
||||||
return new Android11CloseGuard(); |
|
||||||
} else { |
|
||||||
return new AndroidRefCloseGuard(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Initializes the instance with a warning that the caller should have explicitly called the |
|
||||||
* {@code closeMethodName} method instead of relying on finalization. |
|
||||||
* |
|
||||||
* @param closeMethodName non-null name of explicit termination method. Printed by warnIfOpen. |
|
||||||
* @throws NullPointerException if closeMethodName is null. |
|
||||||
*/ |
|
||||||
void open(@NonNull String closeMethodName); |
|
||||||
|
|
||||||
/** |
|
||||||
* Marks this CloseGuard instance as closed to avoid warnings on finalization. |
|
||||||
*/ |
|
||||||
void close(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Logs a warning if the caller did not properly cleanup by calling an explicit close method |
|
||||||
* before finalization. |
|
||||||
*/ |
|
||||||
void warnIfOpen(); |
|
||||||
} |
|
@ -1,83 +0,0 @@ |
|||||||
package me.ag2s.utils; |
|
||||||
|
|
||||||
import androidx.annotation.NonNull; |
|
||||||
|
|
||||||
import java.lang.reflect.Method; |
|
||||||
|
|
||||||
final class AndroidRefCloseGuard implements AndroidCloseGuard { |
|
||||||
private static Object closeGuardInstance; |
|
||||||
private static Method getMethod; |
|
||||||
private static Method closeMethod; |
|
||||||
private static Method openMethod; |
|
||||||
private static Method warnIfOpenMethod; |
|
||||||
|
|
||||||
|
|
||||||
public AndroidRefCloseGuard() { |
|
||||||
|
|
||||||
if (getMethod == null || closeMethod == null || openMethod == null || warnIfOpenMethod == null) { |
|
||||||
try { |
|
||||||
Class<?> closeGuardClass = Class.forName("dalvik.system.CloseGuard"); |
|
||||||
getMethod = closeGuardClass.getMethod("get"); |
|
||||||
closeMethod = closeGuardClass.getMethod("close"); |
|
||||||
openMethod = closeGuardClass.getMethod("open", String.class); |
|
||||||
warnIfOpenMethod = closeGuardClass.getMethod("warnIfOpen"); |
|
||||||
} catch (Exception ignored) { |
|
||||||
getMethod = null; |
|
||||||
openMethod = null; |
|
||||||
warnIfOpenMethod = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
Object createAndOpen(String closer) { |
|
||||||
if (getMethod != null) { |
|
||||||
try { |
|
||||||
if (closeGuardInstance == null) { |
|
||||||
closeGuardInstance = getMethod.invoke(null); |
|
||||||
} |
|
||||||
|
|
||||||
openMethod.invoke(closeGuardInstance, closer); |
|
||||||
return closeGuardInstance; |
|
||||||
} catch (Exception ignored) { |
|
||||||
} |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
boolean warnIfOpen(Object closeGuardInstance) { |
|
||||||
boolean reported = false; |
|
||||||
if (closeGuardInstance != null) { |
|
||||||
try { |
|
||||||
warnIfOpenMethod.invoke(closeGuardInstance); |
|
||||||
reported = true; |
|
||||||
} catch (Exception ignored) { |
|
||||||
} |
|
||||||
} |
|
||||||
return reported; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void open(@NonNull String closeMethodName) { |
|
||||||
closeGuardInstance = createAndOpen(closeMethodName); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void close() { |
|
||||||
if (closeGuardInstance != null) { |
|
||||||
try { |
|
||||||
closeMethod.invoke(closeMethod); |
|
||||||
} catch (Exception ignored) { |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void warnIfOpen() { |
|
||||||
warnIfOpen(closeGuardInstance); |
|
||||||
} |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package me.ag2s.utils; |
|
||||||
|
|
||||||
import androidx.annotation.NonNull; |
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
|
|
||||||
public class ThrowableUtils { |
|
||||||
|
|
||||||
|
|
||||||
public static @NonNull |
|
||||||
IOException rethrowAsIOException(Throwable throwable) throws IOException { |
|
||||||
IOException newException = new IOException(throwable.getMessage()); |
|
||||||
newException.initCause(throwable); |
|
||||||
throw newException; |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue