读取效率太低了,偶尔会读取失败,回退到之前的方法。
This commit is contained in:
Generated
-125
@@ -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>
|
|
||||||
@@ -11,7 +11,6 @@ import me.ag2s.epublib.domain.EpubBook
|
|||||||
import me.ag2s.epublib.domain.Resource
|
import me.ag2s.epublib.domain.Resource
|
||||||
import me.ag2s.epublib.domain.TOCReference
|
import me.ag2s.epublib.domain.TOCReference
|
||||||
import me.ag2s.epublib.epub.EpubReader
|
import me.ag2s.epublib.epub.EpubReader
|
||||||
import me.ag2s.epublib.zip.ZipFile
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import org.jsoup.select.Elements
|
import org.jsoup.select.Elements
|
||||||
@@ -22,6 +21,7 @@ import java.io.FileOutputStream
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
class EpubFile(var book: Book) {
|
class EpubFile(var book: Book) {
|
||||||
|
|
||||||
@@ -105,11 +105,12 @@ class EpubFile(var book: Book) {
|
|||||||
try {
|
try {
|
||||||
val uri = Uri.parse(book.bookUrl)
|
val uri = Uri.parse(book.bookUrl)
|
||||||
return if (uri.isContentScheme()) {
|
return if (uri.isContentScheme()) {
|
||||||
//通过懒加载读取epub
|
//高版本的手机内存一般足够大,直接加载到内存中最快
|
||||||
EpubReader().readEpubLazy(ZipFile(appCtx, uri), "utf-8")
|
EpubReader().readEpub(LocalBook.getBookInputStream(book), "utf-8")
|
||||||
} else {
|
} else {
|
||||||
val bis = LocalBook.getBookInputStream(book)
|
//低版本的使用懒加载
|
||||||
EpubReader().readEpub(bis, "utf-8")
|
EpubReader().readEpubLazy(ZipFile(uri.path), "utf-8")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,35 @@
|
|||||||
package me.ag2s.epublib.domain;
|
package me.ag2s.epublib.domain;
|
||||||
|
|
||||||
import android.content.Context;
|
import androidx.annotation.NonNull;
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
import me.ag2s.epublib.zip.ZipEntry;
|
import java.util.zip.ZipFile;
|
||||||
import me.ag2s.epublib.zip.ZipFile;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author jake
|
* @author jake
|
||||||
*/
|
*/
|
||||||
public class EpubResourceProvider implements LazyResourceProvider {
|
public class EpubResourceProvider implements LazyResourceProvider {
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Uri uri;
|
|
||||||
|
|
||||||
/**
|
private final String epubFilename;
|
||||||
* @param context
|
|
||||||
* @param uri
|
|
||||||
*/
|
public EpubResourceProvider(String epubFilename) {
|
||||||
public EpubResourceProvider(Context context, Uri uri) {
|
this.epubFilename = epubFilename;
|
||||||
this.context = context;
|
|
||||||
this.uri = uri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceStream(String href) throws IOException {
|
public InputStream getResourceStream(@NonNull String href) throws IOException {
|
||||||
ZipFile zipFile = new ZipFile(context, uri);
|
|
||||||
|
ZipFile zipFile = new ZipFile(epubFilename);
|
||||||
ZipEntry zipEntry = zipFile.getEntry(href);
|
ZipEntry zipEntry = zipFile.getEntry(href);
|
||||||
if (zipEntry == null) {
|
if (zipEntry == null) {
|
||||||
zipFile.close();
|
zipFile.close();
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Cannot find entry " + href + " in epub file " + uri.toString());
|
"Cannot find entry " + href + " in epub file " + epubFilename);
|
||||||
}
|
}
|
||||||
return new ResourceInputStream(zipFile.getInputStream(zipEntry), zipFile);
|
return new ResourceInputStream(zipFile.getInputStream(zipEntry), zipFile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package me.ag2s.epublib.domain;
|
|||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
import me.ag2s.epublib.zip.ZipFile;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper class for closing a ZipFile object when the InputStream derived
|
* A wrapper class for closing a ZipFile object when the InputStream derived
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package me.ag2s.epublib.epub;
|
|||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
@@ -9,6 +11,8 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import me.ag2s.epublib.Constants;
|
import me.ag2s.epublib.Constants;
|
||||||
import me.ag2s.epublib.domain.EpubBook;
|
import me.ag2s.epublib.domain.EpubBook;
|
||||||
@@ -18,8 +22,6 @@ import me.ag2s.epublib.domain.Resource;
|
|||||||
import me.ag2s.epublib.domain.Resources;
|
import me.ag2s.epublib.domain.Resources;
|
||||||
import me.ag2s.epublib.util.ResourceUtil;
|
import me.ag2s.epublib.util.ResourceUtil;
|
||||||
import me.ag2s.epublib.util.StringUtil;
|
import me.ag2s.epublib.util.StringUtil;
|
||||||
import me.ag2s.epublib.zip.ZipFile;
|
|
||||||
import me.ag2s.epublib.zip.ZipInputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an epub file.
|
* Reads an epub file.
|
||||||
@@ -65,13 +67,13 @@ public class EpubReader {
|
|||||||
* @return this Book without loading all resources into memory.
|
* @return this Book without loading all resources into memory.
|
||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
*/
|
*/
|
||||||
public EpubBook readEpubLazy(ZipFile zipFile, String encoding)
|
public EpubBook readEpubLazy(@NonNull ZipFile zipFile, @NonNull String encoding)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return readEpubLazy(zipFile, encoding,
|
return readEpubLazy(zipFile, encoding,
|
||||||
Arrays.asList(MediaTypes.mediaTypes));
|
Arrays.asList(MediaTypes.mediaTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
public EpubBook readEpub(ZipInputStream in, String encoding) throws IOException {
|
public EpubBook readEpub(@NonNull ZipInputStream in, @NonNull String encoding) throws IOException {
|
||||||
return readEpub(ResourcesLoader.loadResources(in, encoding));
|
return readEpub(ResourcesLoader.loadResources(in, encoding));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +90,8 @@ public class EpubReader {
|
|||||||
* @return this Book without loading all resources into memory.
|
* @return this Book without loading all resources into memory.
|
||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
*/
|
*/
|
||||||
public EpubBook readEpubLazy(ZipFile zipFile, String encoding,
|
public EpubBook readEpubLazy(@NonNull ZipFile zipFile, @NonNull String encoding,
|
||||||
List<MediaType> lazyLoadedTypes) throws IOException {
|
@NonNull List<MediaType> lazyLoadedTypes) throws IOException {
|
||||||
Resources resources = ResourcesLoader
|
Resources resources = ResourcesLoader
|
||||||
.loadResources(zipFile, encoding, lazyLoadedTypes);
|
.loadResources(zipFile, encoding, lazyLoadedTypes);
|
||||||
return readEpub(resources);
|
return readEpub(resources);
|
||||||
@@ -114,7 +116,6 @@ public class EpubReader {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private EpubBook postProcessBook(EpubBook book) {
|
private EpubBook postProcessBook(EpubBook book) {
|
||||||
if (bookProcessor != null) {
|
if (bookProcessor != null) {
|
||||||
book = bookProcessor.processBook(book);
|
book = bookProcessor.processBook(book);
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import me.ag2s.epublib.domain.EpubBook;
|
import me.ag2s.epublib.domain.EpubBook;
|
||||||
import me.ag2s.epublib.domain.MediaTypes;
|
import me.ag2s.epublib.domain.MediaTypes;
|
||||||
import me.ag2s.epublib.domain.Resource;
|
import me.ag2s.epublib.domain.Resource;
|
||||||
import me.ag2s.epublib.util.IOUtil;
|
import me.ag2s.epublib.util.IOUtil;
|
||||||
import me.ag2s.epublib.zip.CRC32;
|
|
||||||
import me.ag2s.epublib.zip.ZipEntry;
|
|
||||||
import me.ag2s.epublib.zip.ZipOutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an epub file. Not thread-safe, single use object.
|
* Generates an epub file. Not thread-safe, single use object.
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import me.ag2s.epublib.Constants;
|
import me.ag2s.epublib.Constants;
|
||||||
import me.ag2s.epublib.domain.Author;
|
import me.ag2s.epublib.domain.Author;
|
||||||
@@ -25,8 +27,6 @@ import me.ag2s.epublib.domain.TOCReference;
|
|||||||
import me.ag2s.epublib.domain.TableOfContents;
|
import me.ag2s.epublib.domain.TableOfContents;
|
||||||
import me.ag2s.epublib.util.ResourceUtil;
|
import me.ag2s.epublib.util.ResourceUtil;
|
||||||
import me.ag2s.epublib.util.StringUtil;
|
import me.ag2s.epublib.util.StringUtil;
|
||||||
import me.ag2s.epublib.zip.ZipEntry;
|
|
||||||
import me.ag2s.epublib.zip.ZipOutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the ncx document as defined by namespace http://www.daisy.org/z3986/2005/ncx/
|
* Writes the ncx document as defined by namespace http://www.daisy.org/z3986/2005/ncx/
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipException;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import me.ag2s.epublib.domain.EpubResourceProvider;
|
import me.ag2s.epublib.domain.EpubResourceProvider;
|
||||||
import me.ag2s.epublib.domain.LazyResource;
|
import me.ag2s.epublib.domain.LazyResource;
|
||||||
@@ -17,10 +21,6 @@ import me.ag2s.epublib.domain.Resource;
|
|||||||
import me.ag2s.epublib.domain.Resources;
|
import me.ag2s.epublib.domain.Resources;
|
||||||
import me.ag2s.epublib.util.CollectionUtil;
|
import me.ag2s.epublib.util.CollectionUtil;
|
||||||
import me.ag2s.epublib.util.ResourceUtil;
|
import me.ag2s.epublib.util.ResourceUtil;
|
||||||
import me.ag2s.epublib.zip.ZipEntry;
|
|
||||||
import me.ag2s.epublib.zip.ZipException;
|
|
||||||
import me.ag2s.epublib.zip.ZipFile;
|
|
||||||
import me.ag2s.epublib.zip.ZipInputStream;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,7 +51,7 @@ public class ResourcesLoader {
|
|||||||
List<MediaType> lazyLoadedTypes) throws IOException {
|
List<MediaType> lazyLoadedTypes) throws IOException {
|
||||||
|
|
||||||
LazyResourceProvider resourceProvider =
|
LazyResourceProvider resourceProvider =
|
||||||
new EpubResourceProvider(zipFile.getContext(), zipFile.getUri());
|
new EpubResourceProvider(zipFile.getName());
|
||||||
|
|
||||||
Resources result = new Resources();
|
Resources result = new Resources();
|
||||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
|
||||||
@@ -18,8 +20,6 @@ import me.ag2s.epublib.domain.MediaType;
|
|||||||
import me.ag2s.epublib.domain.MediaTypes;
|
import me.ag2s.epublib.domain.MediaTypes;
|
||||||
import me.ag2s.epublib.domain.Resource;
|
import me.ag2s.epublib.domain.Resource;
|
||||||
import me.ag2s.epublib.epub.EpubProcessorSupport;
|
import me.ag2s.epublib.epub.EpubProcessorSupport;
|
||||||
import me.ag2s.epublib.zip.ZipEntry;
|
|
||||||
import me.ag2s.epublib.zip.ZipInputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Various resource utility methods
|
* Various resource utility methods
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user