aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2017-10-13 19:14:24 +0300
committerLasse Collin <lasse.collin@tukaani.org>2017-12-28 19:52:16 +0200
commitc5569e41b895c1a47ecf751f07287861c62f9ced (patch)
treed2ef285f0ba2861a85daf8482854fc6f5cba6eee
parentd91f7661b6b6da431d5d7fc037451a913d749ee0 (diff)
downloadxz-java-c5569e41b895c1a47ecf751f07287861c62f9ced.tar.gz
Add ArrayCache support to LZMA and LZMA2 coders, part 1.
This assumes that the sizes of the arrays from the cache will match the requested size. This works with BasicArrayCache but isn't compliant with the ArrayCache API yet.
-rw-r--r--src/org/tukaani/xz/LZMA2Decoder.java2
-rw-r--r--src/org/tukaani/xz/LZMA2InputStream.java49
-rw-r--r--src/org/tukaani/xz/LZMA2Options.java6
-rw-r--r--src/org/tukaani/xz/LZMA2OutputStream.java23
-rw-r--r--src/org/tukaani/xz/LZMAInputStream.java210
-rw-r--r--src/org/tukaani/xz/LZMAOutputStream.java78
-rw-r--r--src/org/tukaani/xz/UncompressedLZMA2OutputStream.java16
-rw-r--r--src/org/tukaani/xz/lz/BT4.java18
-rw-r--r--src/org/tukaani/xz/lz/HC4.java18
-rw-r--r--src/org/tukaani/xz/lz/Hash234.java19
-rw-r--r--src/org/tukaani/xz/lz/LZDecoder.java9
-rw-r--r--src/org/tukaani/xz/lz/LZEncoder.java19
-rw-r--r--src/org/tukaani/xz/lzma/LZMAEncoder.java14
-rw-r--r--src/org/tukaani/xz/lzma/LZMAEncoderFast.java6
-rw-r--r--src/org/tukaani/xz/lzma/LZMAEncoderNormal.java6
-rw-r--r--src/org/tukaani/xz/rangecoder/RangeDecoderFromBuffer.java9
-rw-r--r--src/org/tukaani/xz/rangecoder/RangeEncoderToBuffer.java9
17 files changed, 449 insertions, 62 deletions
diff --git a/src/org/tukaani/xz/LZMA2Decoder.java b/src/org/tukaani/xz/LZMA2Decoder.java
index 8e777fe..71c1c90 100644
--- a/src/org/tukaani/xz/LZMA2Decoder.java
+++ b/src/org/tukaani/xz/LZMA2Decoder.java
@@ -30,6 +30,6 @@ class LZMA2Decoder extends LZMA2Coder implements FilterDecoder {
}
public InputStream getInputStream(InputStream in, ArrayCache arrayCache) {
- return new LZMA2InputStream(in, dictSize);
+ return new LZMA2InputStream(in, dictSize, null, arrayCache);
}
}
diff --git a/src/org/tukaani/xz/LZMA2InputStream.java b/src/org/tukaani/xz/LZMA2InputStream.java
index bcc4919..9708052 100644
--- a/src/org/tukaani/xz/LZMA2InputStream.java
+++ b/src/org/tukaani/xz/LZMA2InputStream.java
@@ -43,11 +43,11 @@ public class LZMA2InputStream extends InputStream {
private static final int COMPRESSED_SIZE_MAX = 1 << 16;
+ private final ArrayCache arrayCache;
private DataInputStream in;
- private final LZDecoder lz;
- private final RangeDecoderFromBuffer rc
- = new RangeDecoderFromBuffer(COMPRESSED_SIZE_MAX);
+ private LZDecoder lz;
+ private RangeDecoderFromBuffer rc;
private LZMADecoder lzma;
private int uncompressedSize = 0;
@@ -136,13 +136,41 @@ public class LZMA2InputStream extends InputStream {
* to use no preset dictionary
*/
public LZMA2InputStream(InputStream in, int dictSize, byte[] presetDict) {
+ this(in, dictSize, presetDict, ArrayCache.getDefaultCache());
+ }
+
+ /**
+ * Creates a new LZMA2 decompressor using a preset dictionary
+ * and array cache.
+ * <p>
+ * This is like <code>LZMA2InputStream(InputStream, int, byte[])</code>
+ * except that this also takes the <code>arrayCache</code> argument.
+ *
+ * @param in input stream from which LZMA2-compressed
+ * data is read
+ *
+ * @param dictSize LZMA2 dictionary size as bytes, must be
+ * in the range [<code>DICT_SIZE_MIN</code>,
+ * <code>DICT_SIZE_MAX</code>]
+ *
+ * @param presetDict preset dictionary or <code>null</code>
+ * to use no preset dictionary
+ *
+ * @param arrayCache cache to be used for allocating large arrays
+ *
+ * @since 1.7
+ */
+ LZMA2InputStream(InputStream in, int dictSize, byte[] presetDict,
+ ArrayCache arrayCache) {
// Check for null because otherwise null isn't detect
// in this constructor.
if (in == null)
throw new NullPointerException();
+ this.arrayCache = arrayCache;
this.in = new DataInputStream(in);
- this.lz = new LZDecoder(getDictSize(dictSize), presetDict);
+ this.rc = new RangeDecoderFromBuffer(COMPRESSED_SIZE_MAX, arrayCache);
+ this.lz = new LZDecoder(getDictSize(dictSize), presetDict, arrayCache);
if (presetDict != null && presetDict.length > 0)
needDictReset = false;
@@ -254,6 +282,7 @@ public class LZMA2InputStream extends InputStream {
if (control == 0x00) {
endReached = true;
+ putArraysToCache();
return;
}
@@ -341,6 +370,16 @@ public class LZMA2InputStream extends InputStream {
: Math.min(uncompressedSize, in.available());
}
+ private void putArraysToCache() {
+ if (lz != null) {
+ lz.putArraysToCache(arrayCache);
+ lz = null;
+
+ rc.putArraysToCache(arrayCache);
+ rc = null;
+ }
+ }
+
/**
* Closes the stream and calls <code>in.close()</code>.
* If the stream was already closed, this does nothing.
@@ -349,6 +388,8 @@ public class LZMA2InputStream extends InputStream {
*/
public void close() throws IOException {
if (in != null) {
+ putArraysToCache();
+
try {
in.close();
} finally {
diff --git a/src/org/tukaani/xz/LZMA2Options.java b/src/org/tukaani/xz/LZMA2Options.java
index 11d4870..21e186e 100644
--- a/src/org/tukaani/xz/LZMA2Options.java
+++ b/src/org/tukaani/xz/LZMA2Options.java
@@ -532,9 +532,9 @@ public class LZMA2Options extends FilterOptions {
public FinishableOutputStream getOutputStream(FinishableOutputStream out,
ArrayCache arrayCache) {
if (mode == MODE_UNCOMPRESSED)
- return new UncompressedLZMA2OutputStream(out);
+ return new UncompressedLZMA2OutputStream(out, arrayCache);
- return new LZMA2OutputStream(out, this);
+ return new LZMA2OutputStream(out, this, arrayCache);
}
/**
@@ -565,7 +565,7 @@ public class LZMA2Options extends FilterOptions {
public InputStream getInputStream(InputStream in, ArrayCache arrayCache)
throws IOException {
- return new LZMA2InputStream(in, dictSize, presetDict);
+ return new LZMA2InputStream(in, dictSize, presetDict, arrayCache);
}
FilterEncoder getFilterEncoder() {
diff --git a/src/org/tukaani/xz/LZMA2OutputStream.java b/src/org/tukaani/xz/LZMA2OutputStream.java
index fa26a30..a82a1a5 100644
--- a/src/org/tukaani/xz/LZMA2OutputStream.java
+++ b/src/org/tukaani/xz/LZMA2OutputStream.java
@@ -19,12 +19,14 @@ import org.tukaani.xz.lzma.LZMAEncoder;
class LZMA2OutputStream extends FinishableOutputStream {
static final int COMPRESSED_SIZE_MAX = 64 << 10;
+ private final ArrayCache arrayCache;
+
private FinishableOutputStream out;
private final DataOutputStream outData;
- private final LZEncoder lz;
- private final RangeEncoderToBuffer rc;
- private final LZMAEncoder lzma;
+ private LZEncoder lz;
+ private RangeEncoderToBuffer rc;
+ private LZMAEncoder lzma;
private final int props; // Cannot change props on the fly for now.
private boolean dictResetNeeded = true;
@@ -51,13 +53,15 @@ class LZMA2OutputStream extends FinishableOutputStream {
options.getMatchFinder());
}
- LZMA2OutputStream(FinishableOutputStream out, LZMA2Options options) {
+ LZMA2OutputStream(FinishableOutputStream out, LZMA2Options options,
+ ArrayCache arrayCache) {
if (out == null)
throw new NullPointerException();
+ this.arrayCache = arrayCache;
this.out = out;
outData = new DataOutputStream(out);
- rc = new RangeEncoderToBuffer(COMPRESSED_SIZE_MAX);
+ rc = new RangeEncoderToBuffer(COMPRESSED_SIZE_MAX, arrayCache);
int dictSize = options.getDictSize();
int extraSizeBefore = getExtraSizeBefore(dictSize);
@@ -65,7 +69,8 @@ class LZMA2OutputStream extends FinishableOutputStream {
options.getLc(), options.getLp(), options.getPb(),
options.getMode(),
dictSize, extraSizeBefore, options.getNiceLen(),
- options.getMatchFinder(), options.getDepthLimit());
+ options.getMatchFinder(), options.getDepthLimit(),
+ this.arrayCache);
lz = lzma.getLZEncoder();
@@ -198,6 +203,12 @@ class LZMA2OutputStream extends FinishableOutputStream {
}
finished = true;
+
+ lzma.putArraysToCache(arrayCache);
+ lzma = null;
+ lz = null;
+ rc.putArraysToCache(arrayCache);
+ rc = null;
}
public void flush() throws IOException {
diff --git a/src/org/tukaani/xz/LZMAInputStream.java b/src/org/tukaani/xz/LZMAInputStream.java
index 9bbd261..e46d5bb 100644
--- a/src/org/tukaani/xz/LZMAInputStream.java
+++ b/src/org/tukaani/xz/LZMAInputStream.java
@@ -47,6 +47,7 @@ public class LZMAInputStream extends InputStream {
public static final int DICT_SIZE_MAX = Integer.MAX_VALUE & ~15;
private InputStream in;
+ private ArrayCache arrayCache;
private LZDecoder lz;
private RangeDecoderFromStream rc;
private LZMADecoder lzma;
@@ -183,6 +184,42 @@ public class LZMAInputStream extends InputStream {
}
/**
+ * Creates a new .lzma file format decompressor without
+ * a memory usage limit.
+ * <p>
+ * This is identical to <code>LZMAInputStream(InputStream)</code>
+ * except that this also takes the <code>arrayCache</code> argument.
+ *
+ * @param in input stream from which .lzma data is read;
+ * it might be a good idea to wrap it in
+ * <code>BufferedInputStream</code>, see the
+ * note at the top of this page
+ *
+ *
+ * @param arrayCache cache to be used for allocating large arrays
+ *
+ * @throws CorruptedInputException
+ * file is corrupt or perhaps not in
+ * the .lzma format at all
+ *
+ * @throws UnsupportedOptionsException
+ * dictionary size or uncompressed size is too
+ * big for this implementation
+ *
+ * @throws EOFException
+ * file is truncated or perhaps not in
+ * the .lzma format at all
+ *
+ * @throws IOException may be thrown by <code>in</code>
+ *
+ * @since 1.7
+ */
+ public LZMAInputStream(InputStream in, ArrayCache arrayCache)
+ throws IOException {
+ this(in, -1, arrayCache);
+ }
+
+ /**
* Creates a new .lzma file format decompressor with an optional
* memory usage limit.
*
@@ -214,6 +251,48 @@ public class LZMAInputStream extends InputStream {
*/
public LZMAInputStream(InputStream in, int memoryLimit)
throws IOException {
+ this(in, memoryLimit, ArrayCache.getDefaultCache());
+ }
+
+ /**
+ * Creates a new .lzma file format decompressor with an optional
+ * memory usage limit.
+ * <p>
+ * This is identical to <code>LZMAInputStream(InputStream, int)</code>
+ * except that this also takes the <code>arrayCache</code> argument.
+ *
+ * @param in input stream from which .lzma data is read;
+ * it might be a good idea to wrap it in
+ * <code>BufferedInputStream</code>, see the
+ * note at the top of this page
+ *
+ * @param memoryLimit memory usage limit in kibibytes (KiB)
+ * or <code>-1</code> to impose no
+ * memory usage limit
+ *
+ * @param arrayCache cache to be used for allocating large arrays
+ *
+ * @throws CorruptedInputException
+ * file is corrupt or perhaps not in
+ * the .lzma format at all
+ *
+ * @throws UnsupportedOptionsException
+ * dictionary size or uncompressed size is too
+ * big for this implementation
+ *
+ * @throws MemoryLimitException
+ * memory usage limit was exceeded
+ *
+ * @throws EOFException
+ * file is truncated or perhaps not in
+ * the .lzma format at all
+ *
+ * @throws IOException may be thrown by <code>in</code>
+ *
+ * @since 1.7
+ */
+ public LZMAInputStream(InputStream in, int memoryLimit,
+ ArrayCache arrayCache) throws IOException {
DataInputStream inData = new DataInputStream(in);
// Properties byte (lc, lp, and pb)
@@ -237,7 +316,7 @@ public class LZMAInputStream extends InputStream {
if (memoryLimit != -1 && memoryNeeded > memoryLimit)
throw new MemoryLimitException(memoryNeeded, memoryLimit);
- initialize(in, uncompSize, propsByte, dictSize, null);
+ initialize(in, uncompSize, propsByte, dictSize, null, arrayCache);
}
/**
@@ -288,7 +367,8 @@ public class LZMAInputStream extends InputStream {
*/
public LZMAInputStream(InputStream in, long uncompSize, byte propsByte,
int dictSize) throws IOException {
- initialize(in, uncompSize, propsByte, dictSize, null);
+ initialize(in, uncompSize, propsByte, dictSize, null,
+ ArrayCache.getDefaultCache());
}
/**
@@ -326,7 +406,56 @@ public class LZMAInputStream extends InputStream {
public LZMAInputStream(InputStream in, long uncompSize, byte propsByte,
int dictSize, byte[] presetDict)
throws IOException {
- initialize(in, uncompSize, propsByte, dictSize, presetDict);
+ initialize(in, uncompSize, propsByte, dictSize, presetDict,
+ ArrayCache.getDefaultCache());
+ }
+
+ /**
+ * Creates a new input stream that decompresses raw LZMA data (no .lzma
+ * header) from <code>in</code> optionally with a preset dictionary.
+ * <p>
+ * This is identical to <code>LZMAInputStream(InputStream, long, byte, int,
+ * byte[])</code> except that this also takes the <code>arrayCache</code>
+ * argument.
+ *
+ * @param in input stream from which LZMA-compressed
+ * data is read
+ *
+ * @param uncompSize uncompressed size of the LZMA stream or -1
+ * if the end marker is used in the LZMA stream
+ *
+ * @param propsByte LZMA properties byte that has the encoded
+ * values for literal context bits (lc), literal
+ * position bits (lp), and position bits (pb)
+ *
+ * @param dictSize dictionary size as bytes, must be in the range
+ * [<code>0</code>, <code>DICT_SIZE_MAX</code>]
+ *
+ * @param presetDict preset dictionary or <code>null</code>
+ * to use no preset dictionary
+ *
+ * @param arrayCache cache to be used for allocating large arrays
+ *
+ * @throws CorruptedInputException
+ * if <code>propsByte</code> is invalid or
+ * the first input byte is not 0x00
+ *
+ * @throws UnsupportedOptionsException
+ * dictionary size or uncompressed size is too
+ * big for this implementation
+ *
+ * @throws EOFException file is truncated or corrupt
+ *
+ * @throws IOException may be thrown by <code>in</code>
+ *
+ * @since 1.7
+ */
+ public LZMAInputStream(InputStream in, long uncompSize, byte propsByte,
+ int dictSize, byte[] presetDict,
+ ArrayCache arrayCache)
+ throws IOException {
+ initialize(in, uncompSize, propsByte, dictSize, presetDict,
+ arrayCache);
}
/**
@@ -365,11 +494,62 @@ public class LZMAInputStream extends InputStream {
int lc, int lp, int pb,
int dictSize, byte[] presetDict)
throws IOException {
- initialize(in, uncompSize, lc, lp, pb, dictSize, presetDict);
+ initialize(in, uncompSize, lc, lp, pb, dictSize, presetDict,
+ ArrayCache.getDefaultCache());
+ }
+
+ /**
+ * Creates a new input stream that decompresses raw LZMA data (no .lzma
+ * header) from <code>in</code> optionally with a preset dictionary.
+ * <p>
+ * This is identical to <code>LZMAInputStream(InputStream, long, int, int,
+ * int, int, byte[])</code> except that this also takes the
+ * <code>arrayCache</code> argument.
+ *
+ * @param in input stream from which LZMA-compressed
+ * data is read
+ *
+ * @param uncompSize uncompressed size of the LZMA stream or -1
+ * if the end marker is used in the LZMA stream
+ *
+ * @param lc number of literal context bits, must be
+ * in the range [0, 8]
+ *
+ * @param lp number of literal position bits, must be
+ * in the range [0, 4]
+ *
+ * @param pb number position bits, must be
+ * in the range [0, 4]
+ *
+ * @param dictSize dictionary size as bytes, must be in the range
+ * [<code>0</code>, <code>DICT_SIZE_MAX</code>]
+ *
+ * @param presetDict preset dictionary or <code>null</code>
+ * to use no preset dictionary
+ *
+ * @param arrayCache cache to be used for allocating large arrays
+ *
+ * @throws CorruptedInputException
+ * if the first input byte is not 0x00
+ *
+ * @throws EOFException file is truncated or corrupt
+ *
+ * @throws IOException may be thrown by <code>in</code>
+ *
+ * @since 1.7
+ */
+ public LZMAInputStream(InputStream in, long uncompSize,
+ int lc, int lp, int pb,
+ int dictSize, byte[] presetDict,
+ ArrayCache arrayCache)
+ throws IOException {
+ initialize(in, uncompSize, lc, lp, pb, dictSize, presetDict,
+ arrayCache);
}
private void initialize(InputStream in, long uncompSize, byte propsByte,
- int dictSize, byte[] presetDict)
+ int dictSize, byte[] presetDict,
+ ArrayCache arrayCache)
throws IOException {
// Validate the uncompressed size since the other "initialize" throws
// IllegalArgumentException if uncompSize < -1.
@@ -394,12 +574,14 @@ public class LZMAInputStream extends InputStream {
throw new UnsupportedOptionsException(
"LZMA dictionary is too big for this implementation");
- initialize(in, uncompSize, lc, lp, pb, dictSize, presetDict);
+ initialize(in, uncompSize, lc, lp, pb, dictSize, presetDict,
+ arrayCache);
}
private void initialize(InputStream in, long uncompSize,
int lc, int lp, int pb,
- int dictSize, byte[] presetDict)
+ int dictSize, byte[] presetDict,
+ ArrayCache arrayCache)
throws IOException {
// getDictSize validates dictSize and gives a message in
// the exception too, so skip validating dictSize here.
@@ -408,6 +590,7 @@ public class LZMAInputStream extends InputStream {
throw new IllegalArgumentException();
this.in = in;
+ this.arrayCache = arrayCache;
// If uncompressed size is known, use it to avoid wasting memory for
// a uselessly large dictionary buffer.
@@ -415,9 +598,10 @@ public class LZMAInputStream extends InputStream {
if (uncompSize >= 0 && dictSize > uncompSize)
dictSize = getDictSize((int)uncompSize);
- lz = new LZDecoder(getDictSize(dictSize), presetDict);
+ lz = new LZDecoder(getDictSize(dictSize), presetDict, arrayCache);
rc = new RangeDecoderFromStream(in);
lzma = new LZMADecoder(lz, rc, lc, lp, pb);
+
remainingSize = uncompSize;
}
@@ -539,6 +723,7 @@ public class LZMAInputStream extends InputStream {
if (!rc.isFinished() || lz.hasPending())
throw new CorruptedInputException();
+ putArraysToCache();
return size == 0 ? -1 : size;
}
}
@@ -551,6 +736,13 @@ public class LZMAInputStream extends InputStream {
}
}
+ private void putArraysToCache() {
+ if (lz != null) {
+ lz.putArraysToCache(arrayCache);
+ lz = null;
+ }
+ }
+
/**
* Closes the stream and calls <code>in.close()</code>.
* If the stream was already closed, this does nothing.
@@ -559,6 +751,8 @@ public class LZMAInputStream extends InputStream {
*/
public void close() throws IOException {
if (in != null) {
+ putArraysToCache();
+
try {
in.close();
} finally {
diff --git a/src/org/tukaani/xz/LZMAOutputStream.java b/src/org/tukaani/xz/LZMAOutputStream.java
index a9f1918..3a1b7b1 100644
--- a/src/org/tukaani/xz/LZMAOutputStream.java
+++ b/src/org/tukaani/xz/LZMAOutputStream.java
@@ -24,9 +24,11 @@ import org.tukaani.xz.lzma.LZMAEncoder;
public class LZMAOutputStream extends FinishableOutputStream {
private OutputStream out;
- private final LZEncoder lz;
+ private final ArrayCache arrayCache;
+
+ private LZEncoder lz;
private final RangeEncoderToStream rc;
- private final LZMAEncoder lzma;
+ private LZMAEncoder lzma;
private final int props;
private final boolean useEndMarker;
@@ -40,7 +42,8 @@ public class LZMAOutputStream extends FinishableOutputStream {
private LZMAOutputStream(OutputStream out, LZMA2Options options,
boolean useHeader, boolean useEndMarker,
- long expectedUncompressedSize)
+ long expectedUncompressedSize,
+ ArrayCache arrayCache)
throws IOException {
if (out == null)
throw new NullPointerException();
@@ -53,6 +56,8 @@ public class LZMAOutputStream extends FinishableOutputStream {
this.useEndMarker = useEndMarker;
this.expectedUncompressedSize = expectedUncompressedSize;
+ this.arrayCache = arrayCache;
+
this.out = out;
rc = new RangeEncoderToStream(out);
@@ -61,7 +66,8 @@ public class LZMAOutputStream extends FinishableOutputStream {
options.getLc(), options.getLp(), options.getPb(),
options.getMode(),
dictSize, 0, options.getNiceLen(),
- options.getMatchFinder(), options.getDepthLimit());
+ options.getMatchFinder(), options.getDepthLimit(),
+ arrayCache);
lz = lzma.getLZEncoder();
@@ -121,7 +127,35 @@ public class LZMAOutputStream extends FinishableOutputStream {
public LZMAOutputStream(OutputStream out, LZMA2Options options,
long inputSize)
throws IOException {
- this(out, options, true, inputSize == -1, inputSize);
+ this(out, options, inputSize, ArrayCache.getDefaultCache());
+ }
+
+ /**
+ * Creates a new compressor for the legacy .lzma file format.
+ * <p>
+ * This is identical to
+ * <code>LZMAOutputStream(OutputStream, LZMA2Options, long)</code>
+ * except that this also takes the <code>arrayCache</code> argument.
+ *
+ * @param out output stream to which the compressed data
+ * will be written
+ *
+ * @param options LZMA compression options; the same class
+ * is used here as is for LZMA2
+ *
+ * @param inputSize uncompressed size of the data to be compressed;
+ * use <code>-1</code> when unknown
+ *
+ * @param arrayCache cache to be used for allocating large arrays
+ *
+ * @throws IOException may be thrown from <code>out</code>
+ *
+ * @since 1.7
+ */
+ public LZMAOutputStream(OutputStream out, LZMA2Options options,
+ long inputSize, ArrayCache arrayCache)
+ throws IOException {
+ this(out, options, true, inputSize == -1, inputSize, arrayCache);
}
/**
@@ -145,7 +179,35 @@ public class LZMAOutputStream extends FinishableOutputStream {
*/
public LZMAOutputStream(OutputStream out, LZMA2Options options,
boolean useEndMarker) throws IOException {
- this(out, options, false, useEndMarker, -1);
+ this(out, options, useEndMarker, ArrayCache.getDefaultCache());
+ }
+
+ /**
+ * Creates a new compressor for raw LZMA (also known as LZMA1) stream.
+ * <p>
+ * This is identical to
+ * <code>LZMAOutputStream(OutputStream, LZMA2Options, boolean)</code>
+ * except that this also takes the <code>arrayCache</code> argument.
+ *
+ * @param out output stream to which the compressed data
+ * will be written
+ *
+ * @param options LZMA compression options; the same class
+ * is used here as is for LZMA2
+ *
+ * @param useEndMarker
+ * if end of stream marker should be written
+ *
+ * @param arrayCache cache to be used for allocating large arrays
+ *
+ * @throws IOException may be thrown from <code>out</code>
+ *
+ * @since 1.7
+ */
+ public LZMAOutputStream(OutputStream out, LZMA2Options options,
+ boolean useEndMarker, ArrayCache arrayCache)
+ throws IOException {
+ this(out, options, false, useEndMarker, -1, arrayCache);
}
/**
@@ -237,6 +299,10 @@ public class LZMAOutputStream extends FinishableOutputStream {
}
finished = true;
+
+ lzma.putArraysToCache(arrayCache);
+ lzma = null;
+ lz = null;
}
}
diff --git a/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java b/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java
index 2145de3..45e1a2f 100644
--- a/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java
+++ b/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java
@@ -13,11 +13,12 @@ import java.io.DataOutputStream;
import java.io.IOException;
class UncompressedLZMA2OutputStream extends FinishableOutputStream {
+ private final ArrayCache arrayCache;
+
private FinishableOutputStream out;
private final DataOutputStream outData;
- private final byte[] uncompBuf
- = new byte[LZMA2OutputStream.COMPRESSED_SIZE_MAX];
+ private final byte[] uncompBuf;
private int uncompPos = 0;
private boolean dictResetNeeded = true;
@@ -31,12 +32,20 @@ class UncompressedLZMA2OutputStream extends FinishableOutputStream {
return 70;
}
- UncompressedLZMA2OutputStream(FinishableOutputStream out) {
+ UncompressedLZMA2OutputStream(FinishableOutputStream out,
+ ArrayCache arrayCache) {
if (out == null)
throw new NullPointerException();
this.out = out;
outData = new DataOutputStream(out);
+
+ // We only allocate one array from the cache. We will call
+ // putArray directly in writeEndMarker and thus we don't use
+ // ResettableArrayCache here.
+ this.arrayCache = arrayCache;
+ uncompBuf = arrayCache.getByteArray(
+ LZMA2OutputStream.COMPRESSED_SIZE_MAX, false);
}
public void write(int b) throws IOException {
@@ -96,6 +105,7 @@ class UncompressedLZMA2OutputStream extends FinishableOutputStream {
}
finished = true;
+ arrayCache.putArray(uncompBuf);
}
public void flush() throws IOException {
diff --git a/src/org/tukaani/xz/lz/BT4.java b/src/org/tukaani/xz/lz/BT4.java
index a73b666..d0116dd 100644
--- a/src/org/tukaani/xz/lz/BT4.java
+++ b/src/org/tukaani/xz/lz/BT4.java
@@ -10,6 +10,8 @@
package org.tukaani.xz.lz;
+import org.tukaani.xz.ArrayCache;
+
final class BT4 extends LZEncoder {
private final Hash234 hash;
private final int[] tree;
@@ -25,14 +27,16 @@ final class BT4 extends LZEncoder {
}
BT4(int dictSize, int beforeSizeMin, int readAheadMax,
- int niceLen, int matchLenMax, int depthLimit) {
- super(dictSize, beforeSizeMin, readAheadMax, niceLen, matchLenMax);
+ int niceLen, int matchLenMax, int depthLimit,
+ ArrayCache arrayCache) {
+ super(dictSize, beforeSizeMin, readAheadMax, niceLen, matchLenMax,
+ arrayCache);
cyclicSize = dictSize + 1;
lzPos = cyclicSize;
- hash = new Hash234(dictSize);
- tree = new int[cyclicSize * 2];
+ hash = new Hash234(dictSize, arrayCache);
+ tree = arrayCache.getIntArray(cyclicSize * 2, false);
// Substracting 1 because the shortest match that this match
// finder can find is 2 bytes, so there's no need to reserve
@@ -42,6 +46,12 @@ final class BT4 extends LZEncoder {
this.depthLimit = depthLimit > 0 ? depthLimit : 16 + niceLen / 2;
}
+ public void putArraysToCache(ArrayCache arrayCache) {
+ arrayCache.putArray(tree);
+ hash.putArraysToCache(arrayCache);
+ super.putArraysToCache(arrayCache);
+ }
+
private int movePos() {
int avail = movePos(niceLen, 4);
diff --git a/src/org/tukaani/xz/lz/HC4.java b/src/org/tukaani/xz/lz/HC4.java
index 0f49fd4..3643609 100644
--- a/src/org/tukaani/xz/lz/HC4.java
+++ b/src/org/tukaani/xz/lz/HC4.java
@@ -10,6 +10,8 @@
package org.tukaani.xz.lz;
+import org.tukaani.xz.ArrayCache;
+
final class HC4 extends LZEncoder {
private final Hash234 hash;
private final int[] chain;
@@ -32,14 +34,16 @@ final class HC4 extends LZEncoder {
* See <code>LZEncoder.getInstance</code> for parameter descriptions.
*/
HC4(int dictSize, int beforeSizeMin, int readAheadMax,
- int niceLen, int matchLenMax, int depthLimit) {
- super(dictSize, beforeSizeMin, readAheadMax, niceLen, matchLenMax);
+ int niceLen, int matchLenMax, int depthLimit,
+ ArrayCache arrayCache) {
+ super(dictSize, beforeSizeMin, readAheadMax, niceLen, matchLenMax,
+ arrayCache);
- hash = new Hash234(dictSize);
+ hash = new Hash234(dictSize, arrayCache);
// +1 because we need dictSize bytes of history + the current byte.
cyclicSize = dictSize + 1;
- chain = new int[cyclicSize];
+ chain = arrayCache.getIntArray(cyclicSize, false);
lzPos = cyclicSize;
// Substracting 1 because the shortest match that this match
@@ -53,6 +57,12 @@ final class HC4 extends LZEncoder {
this.depthLimit = (depthLimit > 0) ? depthLimit : 4 + niceLen / 4;
}
+ public void putArraysToCache(ArrayCache arrayCache) {
+ arrayCache.putArray(chain);
+ hash.putArraysToCache(arrayCache);
+ super.putArraysToCache(arrayCache);
+ }
+
/**
* Moves to the next byte, checks that there is enough available space,
* and possibly normalizes the hash tables and the hash chain.
diff --git a/src/org/tukaani/xz/lz/Hash234.java b/src/org/tukaani/xz/lz/Hash234.java
index 8253bc0..3c106e2 100644
--- a/src/org/tukaani/xz/lz/Hash234.java
+++ b/src/org/tukaani/xz/lz/Hash234.java
@@ -10,6 +10,8 @@
package org.tukaani.xz.lz;
+import org.tukaani.xz.ArrayCache;
+
final class Hash234 extends CRC32Hash {
private static final int HASH_2_SIZE = 1 << 10;
private static final int HASH_2_MASK = HASH_2_SIZE - 1;
@@ -19,8 +21,8 @@ final class Hash234 extends CRC32Hash {
private final int hash4Mask;
- private final int[] hash2Table = new int[HASH_2_SIZE];
- private final int[] hash3Table = new int[HASH_3_SIZE];
+ private final int[] hash2Table;
+ private final int[] hash3Table;
private final int[] hash4Table;
private int hash2Value = 0;
@@ -47,11 +49,20 @@ final class Hash234 extends CRC32Hash {
/ (1024 / 4) + 4;
}
- Hash234(int dictSize) {
- hash4Table = new int[getHash4Size(dictSize)];
+ Hash234(int dictSize, ArrayCache arrayCache) {
+ hash2Table = arrayCache.getIntArray(HASH_2_SIZE, true);
+ hash3Table = arrayCache.getIntArray(HASH_3_SIZE, true);
+
+ hash4Table = arrayCache.getIntArray(getHash4Size(dictSize), true);
hash4Mask = hash4Table.length - 1;
}
+ void putArraysToCache(ArrayCache arrayCache) {
+ arrayCache.putArray(hash4Table);
+ arrayCache.putArray(hash3Table);
+ arrayCache.putArray(hash2Table);
+ }
+
void calcHashes(byte[] buf, int off) {
int temp = crcTable[buf[off] & 0xFF] ^ (buf[off + 1] & 0xFF);
hash2Value = temp & HASH_2_MASK;
diff --git a/src/org/tukaani/xz/lz/LZDecoder.java b/src/org/tukaani/xz/lz/LZDecoder.java
index 680fec1..b22b791 100644
--- a/src/org/tukaani/xz/lz/LZDecoder.java
+++ b/src/org/tukaani/xz/lz/LZDecoder.java
@@ -12,6 +12,7 @@ package org.tukaani.xz.lz;
import java.io.DataInputStream;
import java.io.IOException;
+import org.tukaani.xz.ArrayCache;
import org.tukaani.xz.CorruptedInputException;
public final class LZDecoder {
@@ -23,8 +24,8 @@ public final class LZDecoder {
private int pendingLen = 0;
private int pendingDist = 0;
- public LZDecoder(int dictSize, byte[] presetDict) {
- buf = new byte[dictSize];
+ public LZDecoder(int dictSize, byte[] presetDict, ArrayCache arrayCache) {
+ buf = arrayCache.getByteArray(dictSize, false);
if (presetDict != null) {
pos = Math.min(presetDict.length, dictSize);
@@ -34,6 +35,10 @@ public final class LZDecoder {
}
}
+ public void putArraysToCache(ArrayCache arrayCache) {
+ arrayCache.putArray(buf);
+ }
+
public void reset() {
start = 0;
pos = 0;
diff --git a/src/org/tukaani/xz/lz/LZEncoder.java b/src/org/tukaani/xz/lz/LZEncoder.java
index 267d7dd..de36ae0 100644
--- a/src/org/tukaani/xz/lz/LZEncoder.java
+++ b/src/org/tukaani/xz/lz/LZEncoder.java
@@ -12,6 +12,7 @@ package org.tukaani.xz.lz;
import java.io.OutputStream;
import java.io.IOException;
+import org.tukaani.xz.ArrayCache;
public abstract class LZEncoder {
public static final int MF_HC4 = 0x04;
@@ -116,15 +117,16 @@ public abstract class LZEncoder {
*/
public static LZEncoder getInstance(
int dictSize, int extraSizeBefore, int extraSizeAfter,
- int niceLen, int matchLenMax, int mf, int depthLimit) {
+ int niceLen, int matchLenMax, int mf, int depthLimit,
+ ArrayCache arrayCache) {
switch (mf) {
case MF_HC4:
return new HC4(dictSize, extraSizeBefore, extraSizeAfter,
- niceLen, matchLenMax, depthLimit);
+ niceLen, matchLenMax, depthLimit, arrayCache);
case MF_BT4:
return new BT4(dictSize, extraSizeBefore, extraSizeAfter,
- niceLen, matchLenMax, depthLimit);
+ niceLen, matchLenMax, depthLimit, arrayCache);
}
throw new IllegalArgumentException();
@@ -134,9 +136,10 @@ public abstract class LZEncoder {
* Creates a new LZEncoder. See <code>getInstance</code>.
*/
LZEncoder(int dictSize, int extraSizeBefore, int extraSizeAfter,
- int niceLen, int matchLenMax) {
- buf = new byte[getBufSize(dictSize, extraSizeBefore, extraSizeAfter,
- matchLenMax)];
+ int niceLen, int matchLenMax, ArrayCache arrayCache) {
+ buf = arrayCache.getByteArray(getBufSize(dictSize, extraSizeBefore,
+ extraSizeAfter, matchLenMax),
+ false);
keepSizeBefore = extraSizeBefore + dictSize;
keepSizeAfter = extraSizeAfter + matchLenMax;
@@ -145,6 +148,10 @@ public abstract class LZEncoder {
this.niceLen = niceLen;
}
+ public void putArraysToCache(ArrayCache arrayCache) {
+ arrayCache.putArray(buf);
+ }
+
/**
* Sets a preset dictionary. If a preset dictionary is wanted, this
* function must be called immediately after creating the LZEncoder
diff --git a/src/org/tukaani/xz/lzma/LZMAEncoder.java b/src/org/tukaani/xz/lzma/LZMAEncoder.java
index b754358..02d5172 100644
--- a/src/org/tukaani/xz/lzma/LZMAEncoder.java
+++ b/src/org/tukaani/xz/lzma/LZMAEncoder.java
@@ -11,6 +11,7 @@
package org.tukaani.xz.lzma;
import java.io.IOException;
+import org.tukaani.xz.ArrayCache;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.lz.Matches;
import org.tukaani.xz.rangecoder.RangeEncoder;
@@ -91,22 +92,29 @@ public abstract class LZMAEncoder extends LZMACoder {
public static LZMAEncoder getInstance(
RangeEncoder rc, int lc, int lp, int pb, int mode,
int dictSize, int extraSizeBefore,
- int niceLen, int mf, int depthLimit) {
+ int niceLen, int mf, int depthLimit,
+ ArrayCache arrayCache) {
switch (mode) {
case MODE_FAST:
return new LZMAEncoderFast(rc, lc, lp, pb,
dictSize, extraSizeBefore,
- niceLen, mf, depthLimit);
+ niceLen, mf, depthLimit,
+ arrayCache);
case MODE_NORMAL:
return new LZMAEncoderNormal(rc, lc, lp, pb,
dictSize, extraSizeBefore,
- niceLen, mf, depthLimit);
+ niceLen, mf, depthLimit,
+ arrayCache);
}
throw new IllegalArgumentException();
}
+ public void putArraysToCache(ArrayCache arrayCache) {
+ lz.putArraysToCache(arrayCache);
+ }
+
/**
* Gets an integer [0, 63] matching the highest two bits of an integer.
* This is like bit scan reverse (BSR) on x86 except that this also
diff --git a/src/org/tukaani/xz/lzma/LZMAEncoderFast.java b/src/org/tukaani/xz/lzma/LZMAEncoderFast.java
index 072dd09..f8230ee 100644
--- a/src/org/tukaani/xz/lzma/LZMAEncoderFast.java
+++ b/src/org/tukaani/xz/lzma/LZMAEncoderFast.java
@@ -10,6 +10,7 @@
package org.tukaani.xz.lzma;
+import org.tukaani.xz.ArrayCache;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.lz.Matches;
import org.tukaani.xz.rangecoder.RangeEncoder;
@@ -28,13 +29,14 @@ final class LZMAEncoderFast extends LZMAEncoder {
LZMAEncoderFast(RangeEncoder rc, int lc, int lp, int pb,
int dictSize, int extraSizeBefore,
- int niceLen, int mf, int depthLimit) {
+ int niceLen, int mf, int depthLimit,
+ ArrayCache arrayCache) {
super(rc, LZEncoder.getInstance(dictSize,
Math.max(extraSizeBefore,
EXTRA_SIZE_BEFORE),
EXTRA_SIZE_AFTER,
niceLen, MATCH_LEN_MAX,
- mf, depthLimit),
+ mf, depthLimit, arrayCache),
lc, lp, pb, dictSize, niceLen);
}
diff --git a/src/org/tukaani/xz/lzma/LZMAEncoderNormal.java b/src/org/tukaani/xz/lzma/LZMAEncoderNormal.java
index 104afe3..8079cd2 100644
--- a/src/org/tukaani/xz/lzma/LZMAEncoderNormal.java
+++ b/src/org/tukaani/xz/lzma/LZMAEncoderNormal.java
@@ -10,6 +10,7 @@
package org.tukaani.xz.lzma;
+import org.tukaani.xz.ArrayCache;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.lz.Matches;
import org.tukaani.xz.rangecoder.RangeEncoder;
@@ -40,13 +41,14 @@ final class LZMAEncoderNormal extends LZMAEncoder {
LZMAEncoderNormal(RangeEncoder rc, int lc, int lp, int pb,
int dictSize, int extraSizeBefore,
- int niceLen, int mf, int depthLimit) {
+ int niceLen, int mf, int depthLimit,
+ ArrayCache arrayCache) {
super(rc, LZEncoder.getInstance(dictSize,
Math.max(extraSizeBefore,
EXTRA_SIZE_BEFORE),
EXTRA_SIZE_AFTER,
niceLen, MATCH_LEN_MAX,
- mf, depthLimit),
+ mf, depthLimit, arrayCache),
lc, lp, pb, dictSize, niceLen);
for (int i = 0; i < OPTS; ++i)
diff --git a/src/org/tukaani/xz/rangecoder/RangeDecoderFromBuffer.java b/src/org/tukaani/xz/rangecoder/RangeDecoderFromBuffer.java
index 58c8b64..c443a1b 100644
--- a/src/org/tukaani/xz/rangecoder/RangeDecoderFromBuffer.java
+++ b/src/org/tukaani/xz/rangecoder/RangeDecoderFromBuffer.java
@@ -12,6 +12,7 @@ package org.tukaani.xz.rangecoder;
import java.io.DataInputStream;
import java.io.IOException;
+import org.tukaani.xz.ArrayCache;
import org.tukaani.xz.CorruptedInputException;
public final class RangeDecoderFromBuffer extends RangeDecoder {
@@ -20,11 +21,15 @@ public final class RangeDecoderFromBuffer extends RangeDecoder {
private final byte[] buf;
private int pos;
- public RangeDecoderFromBuffer(int inputSizeMax) {
- buf = new byte[inputSizeMax - INIT_SIZE];
+ public RangeDecoderFromBuffer(int inputSizeMax, ArrayCache arrayCache) {
+ buf = arrayCache.getByteArray(inputSizeMax - INIT_SIZE, false);
pos = buf.length;
}
+ public void putArraysToCache(ArrayCache arrayCache) {
+ arrayCache.putArray(buf);
+ }
+
public void prepareInputBuffer(DataInputStream in, int len)
throws IOException {
if (len < INIT_SIZE)
diff --git a/src/org/tukaani/xz/rangecoder/RangeEncoderToBuffer.java b/src/org/tukaani/xz/rangecoder/RangeEncoderToBuffer.java
index 6e0a0e8..aee2e3d 100644
--- a/src/org/tukaani/xz/rangecoder/RangeEncoderToBuffer.java
+++ b/src/org/tukaani/xz/rangecoder/RangeEncoderToBuffer.java
@@ -12,16 +12,21 @@ package org.tukaani.xz.rangecoder;
import java.io.OutputStream;
import java.io.IOException;
+import org.tukaani.xz.ArrayCache;
public final class RangeEncoderToBuffer extends RangeEncoder {
private final byte[] buf;
private int bufPos;
- public RangeEncoderToBuffer(int bufSize) {
- buf = new byte[bufSize];
+ public RangeEncoderToBuffer(int bufSize, ArrayCache arrayCache) {
+ buf = arrayCache.getByteArray(bufSize, false);
reset();
}
+ public void putArraysToCache(ArrayCache arrayCache) {
+ arrayCache.putArray(buf);
+ }
+
public void reset() {
super.reset();
bufPos = 0;