Clean tukaani.xz

This commit is contained in:
huangyuhui
2016-01-26 20:54:52 +08:00
parent fee3e5251d
commit d2162f31e4
31 changed files with 1 additions and 4616 deletions

View File

@@ -1,36 +0,0 @@
/*
* ARMOptions
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
import org.tukaani.xz.simple.ARM;
/**
* BCJ filter for little endian ARM instructions.
*/
public class ARMOptions extends BCJOptions {
private static final int ALIGNMENT = 4;
public ARMOptions() {
super(ALIGNMENT);
}
public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
return new SimpleOutputStream(out, new ARM(true, startOffset));
}
public InputStream getInputStream(InputStream in) {
return new SimpleInputStream(in, new ARM(false, startOffset));
}
FilterEncoder getFilterEncoder() {
return new BCJEncoder(this, BCJCoder.ARM_FILTER_ID);
}
}

View File

@@ -1,36 +0,0 @@
/*
* ARMThumbOptions
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
import org.tukaani.xz.simple.ARMThumb;
/**
* BCJ filter for little endian ARM-Thumb instructions.
*/
public class ARMThumbOptions extends BCJOptions {
private static final int ALIGNMENT = 2;
public ARMThumbOptions() {
super(ALIGNMENT);
}
public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
return new SimpleOutputStream(out, new ARMThumb(true, startOffset));
}
public InputStream getInputStream(InputStream in) {
return new SimpleInputStream(in, new ARMThumb(false, startOffset));
}
FilterEncoder getFilterEncoder() {
return new BCJEncoder(this, BCJCoder.ARMTHUMB_FILTER_ID);
}
}

View File

@@ -13,8 +13,6 @@ abstract class BCJCoder implements FilterCoder {
public static final long X86_FILTER_ID = 0x04;
public static final long POWERPC_FILTER_ID = 0x05;
public static final long IA64_FILTER_ID = 0x06;
public static final long ARM_FILTER_ID = 0x07;
public static final long ARMTHUMB_FILTER_ID = 0x08;
public static final long SPARC_FILTER_ID = 0x09;
public static boolean isBCJFilterID(long filterID) {

View File

@@ -52,10 +52,6 @@ class BCJDecoder extends BCJCoder implements FilterDecoder {
simpleFilter = new PowerPC(false, startOffset);
else if (filterID == IA64_FILTER_ID)
simpleFilter = new IA64(false, startOffset);
else if (filterID == ARM_FILTER_ID)
simpleFilter = new ARM(false, startOffset);
else if (filterID == ARMTHUMB_FILTER_ID)
simpleFilter = new ARMThumb(false, startOffset);
else if (filterID == SPARC_FILTER_ID)
simpleFilter = new SPARC(false, startOffset);
else

View File

@@ -159,13 +159,7 @@ class BlockInputStream extends InputStream {
FilterDecoder[] filters = new FilterDecoder[filterIDs.length];
for (int i = 0; i < filters.length; ++i)
if (filterIDs[i] == LZMA2Coder.FILTER_ID)
filters[i] = new LZMA2Decoder(filterProps[i]);
else if (filterIDs[i] == DeltaCoder.FILTER_ID)
filters[i] = new DeltaDecoder(filterProps[i]);
else if (BCJDecoder.isBCJFilterID(filterIDs[i]))
if (BCJDecoder.isBCJFilterID(filterIDs[i]))
filters[i] = new BCJDecoder(filterIDs[i], filterProps[i]);
else

View File

@@ -1,26 +0,0 @@
/*
* DeltaCoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
abstract class DeltaCoder implements FilterCoder {
public static final long FILTER_ID = 0x03;
public boolean changesSize() {
return false;
}
public boolean nonLastOK() {
return true;
}
public boolean lastOK() {
return false;
}
}

View File

@@ -1,34 +0,0 @@
/*
* DeltaDecoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
class DeltaDecoder extends DeltaCoder implements FilterDecoder {
private final int distance;
DeltaDecoder(byte[] props) throws UnsupportedOptionsException {
if (props.length != 1)
throw new UnsupportedOptionsException(
"Unsupported Delta filter properties");
distance = (props[0] & 0xFF) + 1;
}
@Override
public int getMemoryUsage() {
return 1;
}
@Override
public InputStream getInputStream(InputStream in) {
return new DeltaInputStream(in, distance);
}
}

View File

@@ -1,36 +0,0 @@
/*
* DeltaEncoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
class DeltaEncoder extends DeltaCoder implements FilterEncoder {
private final DeltaOptions options;
private final byte[] props = new byte[1];
DeltaEncoder(DeltaOptions options) {
props[0] = (byte) (options.getDistance() - 1);
this.options = (DeltaOptions) options.clone();
}
public long getFilterID() {
return FILTER_ID;
}
public byte[] getFilterProps() {
return props;
}
public boolean supportsFlushing() {
return true;
}
public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
return options.getOutputStream(out);
}
}

View File

@@ -1,147 +0,0 @@
/*
* DeltaInputStream
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
import java.io.IOException;
import org.tukaani.xz.delta.DeltaDecoder;
/**
* Decodes raw Delta-filtered data (no XZ headers).
* <p>
* The delta filter doesn't change the size of the data and thus it cannot have
* an end-of-payload marker. It will simply decode until its input stream
* indicates end of input.
*/
public class DeltaInputStream extends InputStream {
/**
* Smallest supported delta calculation distance.
*/
public static final int DISTANCE_MIN = 1;
/**
* Largest supported delta calculation distance.
*/
public static final int DISTANCE_MAX = 256;
private InputStream in;
private final DeltaDecoder delta;
private IOException exception = null;
private final byte[] tempBuf = new byte[1];
/**
* Creates a new Delta decoder with the given delta calculation distance.
*
* @param in input stream from which Delta filtered data is read
*
* @param distance delta calculation distance, must be in the range
* [<code>DISTANCE_MIN</code>, <code>DISTANCE_MAX</code>]
*/
public DeltaInputStream(InputStream in, int distance) {
// Check for null because otherwise null isn't detect
// in this constructor.
if (in == null)
throw new NullPointerException();
this.in = in;
this.delta = new DeltaDecoder(distance);
}
/**
* Decode the next byte from this input stream.
*
* @return the next decoded byte, or <code>-1</code> to indicate the end of
* input on the input stream <code>in</code>
*
* @throws IOException may be thrown by <code>in</code>
*/
@Override
public int read() throws IOException {
return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF);
}
/**
* Decode into an array of bytes.
* <p>
* This calls <code>in.read(buf, off, len)</code> and defilters the returned
* data.
*
* @param buf target buffer for decoded data
* @param off start offset in <code>buf</code>
* @param len maximum number of bytes to read
*
* @return number of bytes read, or <code>-1</code> to indicate the end of
* the input stream <code>in</code>
*
* @throws XZIOException if the stream has been closed
*
* @throws IOException may be thrown by underlaying input stream
* <code>in</code>
*/
@Override
public int read(byte[] buf, int off, int len) throws IOException {
if (len == 0)
return 0;
if (in == null)
throw new XZIOException("Stream closed");
if (exception != null)
throw exception;
int size;
try {
size = in.read(buf, off, len);
} catch (IOException e) {
exception = e;
throw e;
}
if (size == -1)
return -1;
delta.decode(buf, off, size);
return size;
}
/**
* Calls <code>in.available()</code>.
*
* @return the value returned by <code>in.available()</code>
*/
@Override
public int available() throws IOException {
if (in == null)
throw new XZIOException("Stream closed");
if (exception != null)
throw exception;
return in.available();
}
/**
* Closes the stream and calls <code>in.close()</code>. If the stream was
* already closed, this does nothing.
*
* @throws IOException if thrown by <code>in.close()</code>
*/
@Override
public void close() throws IOException {
if (in != null)
try {
in.close();
} finally {
in = null;
}
}
}

View File

@@ -1,109 +0,0 @@
/*
* DeltaOptions
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
/**
* Delta filter options. The Delta filter can be used only as a non-last filter
* in the chain, for example Delta + LZMA2.
* <p>
* Currently only simple byte-wise delta is supported. The only option is the
* delta distance, which you should set to match your data. It's not possible to
* provide a generic default value for it.
* <p>
* For example, with distance = 2 and eight-byte input A1 B1 A2 B3 A3 B5 A4 B7,
* the output will be A1 B1 01 02 01 02 01 02.
* <p>
* The Delta filter can be good with uncompressed bitmap images. It can also
* help with PCM audio, although special-purpose compressors like FLAC will give
* much smaller result at much better compression speed.
*/
public class DeltaOptions extends FilterOptions {
/**
* Smallest supported delta calculation distance.
*/
public static final int DISTANCE_MIN = 1;
/**
* Largest supported delta calculation distance.
*/
public static final int DISTANCE_MAX = 256;
private int distance = DISTANCE_MIN;
/**
* Creates new Delta options and sets the delta distance to 1 byte.
*/
public DeltaOptions() {
}
/**
* Creates new Delta options and sets the distance to the given value.
*/
public DeltaOptions(int distance) throws UnsupportedOptionsException {
setDistance(distance);
}
/**
* Sets the delta distance in bytes. The new distance must be in the range
* [DISTANCE_MIN, DISTANCE_MAX].
*/
public void setDistance(int distance) throws UnsupportedOptionsException {
if (distance < DISTANCE_MIN || distance > DISTANCE_MAX)
throw new UnsupportedOptionsException(
"Delta distance must be in the range [" + DISTANCE_MIN
+ ", " + DISTANCE_MAX + "]: " + distance);
this.distance = distance;
}
/**
* Gets the delta distance.
*/
public int getDistance() {
return distance;
}
@Override
public int getEncoderMemoryUsage() {
return DeltaOutputStream.getMemoryUsage();
}
@Override
public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
return new DeltaOutputStream(out, this);
}
@Override
public int getDecoderMemoryUsage() {
return 1;
}
@Override
public InputStream getInputStream(InputStream in) {
return new DeltaInputStream(in, distance);
}
@Override
FilterEncoder getFilterEncoder() {
return new DeltaEncoder(this);
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
assert false;
throw new RuntimeException();
}
}
}

View File

@@ -1,113 +0,0 @@
/*
* DeltaOutputStream
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.IOException;
import org.tukaani.xz.delta.DeltaEncoder;
class DeltaOutputStream extends FinishableOutputStream {
private static final int FILTER_BUF_SIZE = 4096;
private FinishableOutputStream out;
private final DeltaEncoder delta;
private final byte[] filterBuf = new byte[FILTER_BUF_SIZE];
private boolean finished = false;
private IOException exception = null;
private final byte[] tempBuf = new byte[1];
static int getMemoryUsage() {
return 1 + FILTER_BUF_SIZE / 1024;
}
DeltaOutputStream(FinishableOutputStream out, DeltaOptions options) {
this.out = out;
delta = new DeltaEncoder(options.getDistance());
}
public void write(int b) throws IOException {
tempBuf[0] = (byte) b;
write(tempBuf, 0, 1);
}
public void write(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished");
try {
while (len > FILTER_BUF_SIZE) {
delta.encode(buf, off, FILTER_BUF_SIZE, filterBuf);
out.write(filterBuf);
off += FILTER_BUF_SIZE;
len -= FILTER_BUF_SIZE;
}
delta.encode(buf, off, len, filterBuf);
out.write(filterBuf, 0, len);
} catch (IOException e) {
exception = e;
throw e;
}
}
public void flush() throws IOException {
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished or closed");
try {
out.flush();
} catch (IOException e) {
exception = e;
throw e;
}
}
public void finish() throws IOException {
if (!finished) {
if (exception != null)
throw exception;
try {
out.finish();
} catch (IOException e) {
exception = e;
throw e;
}
finished = true;
}
}
public void close() throws IOException {
if (out != null) {
try {
out.close();
} catch (IOException e) {
if (exception == null)
exception = e;
}
out = null;
}
if (exception != null)
throw exception;
}
}

View File

@@ -1,26 +0,0 @@
/*
* LZMA2Coder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
abstract class LZMA2Coder implements FilterCoder {
public static final long FILTER_ID = 0x21;
public boolean changesSize() {
return true;
}
public boolean nonLastOK() {
return false;
}
public boolean lastOK() {
return true;
}
}

View File

@@ -1,37 +0,0 @@
/*
* LZMA2Decoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
class LZMA2Decoder extends LZMA2Coder implements FilterDecoder {
private int dictSize;
LZMA2Decoder(byte[] props) throws UnsupportedOptionsException {
// Up to 1.5 GiB dictionary is supported. The bigger ones
// are too big for int.
if (props.length != 1 || (props[0] & 0xFF) > 37)
throw new UnsupportedOptionsException(
"Unsupported LZMA2 properties");
dictSize = 2 | (props[0] & 1);
dictSize <<= (props[0] >>> 1) + 11;
}
@Override
public int getMemoryUsage() {
return LZMA2InputStream.getMemoryUsage(dictSize);
}
@Override
public InputStream getInputStream(InputStream in) {
return new LZMA2InputStream(in, dictSize);
}
}

View File

@@ -1,54 +0,0 @@
/*
* LZMA2Encoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import org.tukaani.xz.lzma.LZMAEncoder;
class LZMA2Encoder extends LZMA2Coder implements FilterEncoder {
private final LZMA2Options options;
private final byte[] props = new byte[1];
LZMA2Encoder(LZMA2Options options) {
if (options.getPresetDict() != null)
throw new IllegalArgumentException(
"XZ doesn't support a preset dictionary for now");
if (options.getMode() == LZMA2Options.MODE_UNCOMPRESSED)
props[0] = (byte) 0;
else {
int d = Math.max(options.getDictSize(),
LZMA2Options.DICT_SIZE_MIN);
props[0] = (byte) (LZMAEncoder.getDistSlot(d - 1) - 23);
}
// Make a private copy so that the caller is free to change its copy.
this.options = (LZMA2Options) options.clone();
}
@Override
public long getFilterID() {
return FILTER_ID;
}
@Override
public byte[] getFilterProps() {
return props;
}
@Override
public boolean supportsFlushing() {
return true;
}
@Override
public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
return options.getOutputStream(out);
}
}

View File

@@ -1,344 +0,0 @@
/*
* LZMA2InputStream
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import org.tukaani.xz.lz.LZDecoder;
import org.tukaani.xz.rangecoder.RangeDecoderFromBuffer;
import org.tukaani.xz.lzma.LZMADecoder;
/**
* Decompresses a raw LZMA2 stream (no XZ headers).
*/
public class LZMA2InputStream extends InputStream {
/**
* Smallest valid LZMA2 dictionary size.
* <p>
* Very tiny dictionaries would be a performance problem, so the minimum is
* 4 KiB.
*/
public static final int DICT_SIZE_MIN = 4096;
/**
* Largest dictionary size supported by this implementation.
* <p>
* The LZMA2 algorithm allows dictionaries up to one byte less than 4 GiB.
* This implementation supports only 16 bytes less than 2 GiB for raw LZMA2
* streams, and for .xz files the maximum is 1.5 GiB. This limitation is due
* to Java using signed 32-bit integers for array indexing. The limitation
* shouldn't matter much in practice since so huge dictionaries are not
* normally used.
*/
public static final int DICT_SIZE_MAX = Integer.MAX_VALUE & ~15;
private static final int COMPRESSED_SIZE_MAX = 1 << 16;
private DataInputStream in;
private final LZDecoder lz;
private final RangeDecoderFromBuffer rc
= new RangeDecoderFromBuffer(COMPRESSED_SIZE_MAX);
private LZMADecoder lzma;
private int uncompressedSize = 0;
private boolean isLZMAChunk;
private boolean needDictReset = true;
private boolean needProps = true;
private boolean endReached = false;
private IOException exception = null;
private final byte[] tempBuf = new byte[1];
/**
* Gets approximate decompressor memory requirements as kibibytes for the
* given dictionary size.
*
* @param dictSize LZMA2 dictionary size as bytes, must be in the range
* [<code>DICT_SIZE_MIN</code>, <code>DICT_SIZE_MAX</code>]
*
* @return approximate memory requirements as kibibytes (KiB)
*/
public static int getMemoryUsage(int dictSize) {
// The base state is around 30-40 KiB (probabilities etc.),
// range decoder needs COMPRESSED_SIZE_MAX bytes for buffering,
// and LZ decoder needs a dictionary buffer.
return 40 + COMPRESSED_SIZE_MAX / 1024 + getDictSize(dictSize) / 1024;
}
private static int getDictSize(int dictSize) {
if (dictSize < DICT_SIZE_MIN || dictSize > DICT_SIZE_MAX)
throw new IllegalArgumentException(
"Unsupported dictionary size " + dictSize);
// Round dictionary size upward to a multiple of 16. This way LZMA
// can use LZDecoder.getPos() for calculating LZMA's posMask.
// Note that this check is needed only for raw LZMA2 streams; it is
// redundant with .xz.
return (dictSize + 15) & ~15;
}
/**
* Creates a new input stream that decompresses raw LZMA2 data from
* <code>in</code>.
* <p>
* The caller needs to know the dictionary size used when compressing; the
* dictionary size isn't stored as part of a raw LZMA2 stream.
* <p>
* Specifying a too small dictionary size will prevent decompressing the
* stream. Specifying a too big dictionary is waste of memory but
* decompression will work.
* <p>
* There is no need to specify a dictionary bigger than the uncompressed
* size of the data even if a bigger dictionary was used when compressing.
* If you know the uncompressed size of the data, this might allow saving
* some memory.
*
* @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>]
*/
public LZMA2InputStream(InputStream in, int dictSize) {
this(in, dictSize, null);
}
/**
* Creates a new LZMA2 decompressor using a preset dictionary.
* <p>
* This is like <code>LZMA2InputStream(InputStream, int)</code> except that
* the dictionary may be initialized using a preset dictionary. If a preset
* dictionary was used when compressing the data, the same preset dictionary
* must be provided when decompressing.
*
* @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
*/
public LZMA2InputStream(InputStream in, int dictSize, byte[] presetDict) {
// Check for null because otherwise null isn't detect
// in this constructor.
if (in == null)
throw new NullPointerException();
this.in = new DataInputStream(in);
this.lz = new LZDecoder(getDictSize(dictSize), presetDict);
if (presetDict != null && presetDict.length > 0)
needDictReset = false;
}
/**
* Decompresses the next byte from this input stream.
* <p>
* Reading lots of data with <code>read()</code> from this input stream may
* be inefficient. Wrap it in <code>java.io.BufferedInputStream</code> if
* you need to read lots of data one byte at a time.
*
* @return the next decompressed byte, or <code>-1</code> to indicate the
* end of the compressed stream
*
* @throws CorruptedInputException
*
* @throws XZIOException if the stream has been closed
*
* @throws EOFException compressed input is truncated or corrupt
*
* @throws IOException may be thrown by <code>in</code>
*/
public int read() throws IOException {
return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF);
}
/**
* Decompresses into an array of bytes.
* <p>
* If <code>len</code> is zero, no bytes are read and <code>0</code> is
* returned. Otherwise this will block until <code>len</code> bytes have
* been decompressed, the end of the LZMA2 stream is reached, or an
* exception is thrown.
*
* @param buf target buffer for uncompressed data
* @param off start offset in <code>buf</code>
* @param len maximum number of uncompressed bytes to read
*
* @return number of bytes read, or <code>-1</code> to indicate the end of
* the compressed stream
*
* @throws CorruptedInputException
*
* @throws XZIOException if the stream has been closed
*
* @throws EOFException compressed input is truncated or corrupt
*
* @throws IOException may be thrown by <code>in</code>
*/
public int read(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
if (len == 0)
return 0;
if (in == null)
throw new XZIOException("Stream closed");
if (exception != null)
throw exception;
if (endReached)
return -1;
try {
int size = 0;
while (len > 0) {
if (uncompressedSize == 0) {
decodeChunkHeader();
if (endReached)
return size == 0 ? -1 : size;
}
int copySizeMax = Math.min(uncompressedSize, len);
if (!isLZMAChunk)
lz.copyUncompressed(in, copySizeMax);
else {
lz.setLimit(copySizeMax);
lzma.decode();
if (!rc.isInBufferOK())
throw new CorruptedInputException();
}
int copiedSize = lz.flush(buf, off);
off += copiedSize;
len -= copiedSize;
size += copiedSize;
uncompressedSize -= copiedSize;
if (uncompressedSize == 0)
if (!rc.isFinished() || lz.hasPending())
throw new CorruptedInputException();
}
return size;
} catch (IOException e) {
exception = e;
throw e;
}
}
private void decodeChunkHeader() throws IOException {
int control = in.readUnsignedByte();
if (control == 0x00) {
endReached = true;
return;
}
if (control >= 0xE0 || control == 0x01) {
needProps = true;
needDictReset = false;
lz.reset();
} else if (needDictReset)
throw new CorruptedInputException();
if (control >= 0x80) {
isLZMAChunk = true;
uncompressedSize = (control & 0x1F) << 16;
uncompressedSize += in.readUnsignedShort() + 1;
int compressedSize = in.readUnsignedShort() + 1;
if (control >= 0xC0) {
needProps = false;
decodeProps();
} else if (needProps)
throw new CorruptedInputException();
else if (control >= 0xA0)
lzma.reset();
rc.prepareInputBuffer(in, compressedSize);
} else if (control > 0x02)
throw new CorruptedInputException();
else {
isLZMAChunk = false;
uncompressedSize = in.readUnsignedShort() + 1;
}
}
private void decodeProps() throws IOException {
int props = in.readUnsignedByte();
if (props > (4 * 5 + 4) * 9 + 8)
throw new CorruptedInputException();
int pb = props / (9 * 5);
props -= pb * 9 * 5;
int lp = props / 9;
int lc = props - lp * 9;
if (lc + lp > 4)
throw new CorruptedInputException();
lzma = new LZMADecoder(lz, rc, lc, lp, pb);
}
/**
* Returns the number of uncompressed bytes that can be read without
* blocking. The value is returned with an assumption that the compressed
* input data will be valid. If the compressed data is corrupt,
* <code>CorruptedInputException</code> may get thrown before the number of
* bytes claimed to be available have been read from this input stream.
* <p>
* In LZMA2InputStream, the return value will be non-zero when the
* decompressor is in the middle of an LZMA2 chunk. The return value will
* then be the number of uncompressed bytes remaining from that chunk.
*
* @return the number of uncompressed bytes that can be read without
* blocking
*/
public int available() throws IOException {
if (in == null)
throw new XZIOException("Stream closed");
if (exception != null)
throw exception;
return uncompressedSize;
}
/**
* Closes the stream and calls <code>in.close()</code>. If the stream was
* already closed, this does nothing.
*
* @throws IOException if thrown by <code>in.close()</code>
*/
public void close() throws IOException {
if (in != null)
try {
in.close();
} finally {
in = null;
}
}
}

View File

@@ -1,562 +0,0 @@
/*
* LZMA2Options
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
import java.io.IOException;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.lzma.LZMAEncoder;
/**
* LZMA2 compression options.
* <p>
* While this allows setting the LZMA2 compression options in detail, often you
* only need <code>LZMA2Options()</code> or <code>LZMA2Options(int)</code>.
*/
public class LZMA2Options extends FilterOptions {
/**
* Minimum valid compression preset level is 0.
*/
public static final int PRESET_MIN = 0;
/**
* Maximum valid compression preset level is 9.
*/
public static final int PRESET_MAX = 9;
/**
* Default compression preset level is 6.
*/
public static final int PRESET_DEFAULT = 6;
/**
* Minimum dictionary size is 4 KiB.
*/
public static final int DICT_SIZE_MIN = 4096;
/**
* Maximum dictionary size for compression is 768 MiB.
* <p>
* The decompressor supports bigger dictionaries, up to almost 2 GiB. With
* HC4 the encoder would support dictionaries bigger than 768 MiB. The 768
* MiB limit comes from the current implementation of BT4 where we would
* otherwise hit the limits of signed ints in array indexing.
* <p>
* If you really need bigger dictionary for decompression, use
* {@link LZMA2InputStream} directly.
*/
public static final int DICT_SIZE_MAX = 768 << 20;
/**
* The default dictionary size is 8 MiB.
*/
public static final int DICT_SIZE_DEFAULT = 8 << 20;
/**
* Maximum value for lc + lp is 4.
*/
public static final int LC_LP_MAX = 4;
/**
* The default number of literal context bits is 3.
*/
public static final int LC_DEFAULT = 3;
/**
* The default number of literal position bits is 0.
*/
public static final int LP_DEFAULT = 0;
/**
* Maximum value for pb is 4.
*/
public static final int PB_MAX = 4;
/**
* The default number of position bits is 2.
*/
public static final int PB_DEFAULT = 2;
/**
* Compression mode: uncompressed. The data is wrapped into a LZMA2 stream
* without compression.
*/
public static final int MODE_UNCOMPRESSED = 0;
/**
* Compression mode: fast. This is usually combined with a hash chain match
* finder.
*/
public static final int MODE_FAST = LZMAEncoder.MODE_FAST;
/**
* Compression mode: normal. This is usually combined with a binary tree
* match finder.
*/
public static final int MODE_NORMAL = LZMAEncoder.MODE_NORMAL;
/**
* Minimum value for <code>niceLen</code> is 8.
*/
public static final int NICE_LEN_MIN = 8;
/**
* Maximum value for <code>niceLen</code> is 273.
*/
public static final int NICE_LEN_MAX = 273;
/**
* Match finder: Hash Chain 2-3-4
*/
public static final int MF_HC4 = LZEncoder.MF_HC4;
/**
* Match finder: Binary tree 2-3-4
*/
public static final int MF_BT4 = LZEncoder.MF_BT4;
private static final int[] presetToDictSize = {
1 << 18, 1 << 20, 1 << 21, 1 << 22, 1 << 22,
1 << 23, 1 << 23, 1 << 24, 1 << 25, 1 << 26 };
private static final int[] presetToDepthLimit = { 4, 8, 24, 48 };
private int dictSize;
private byte[] presetDict = null;
private int lc;
private int lp;
private int pb;
private int mode;
private int niceLen;
private int mf;
private int depthLimit;
/**
* Creates new LZMA2 options and sets them to the default values. This is
* equivalent to <code>LZMA2Options(PRESET_DEFAULT)</code>.
*/
public LZMA2Options() {
try {
setPreset(PRESET_DEFAULT);
} catch (UnsupportedOptionsException e) {
assert false;
throw new RuntimeException();
}
}
/**
* Creates new LZMA2 options and sets them to the given preset.
*
* @throws UnsupportedOptionsException <code>preset</code> is not supported
*/
public LZMA2Options(int preset) throws UnsupportedOptionsException {
setPreset(preset);
}
/**
* Creates new LZMA2 options and sets them to the given custom values.
*
* @throws UnsupportedOptionsException unsupported options were specified
*/
public LZMA2Options(int dictSize, int lc, int lp, int pb, int mode,
int niceLen, int mf, int depthLimit)
throws UnsupportedOptionsException {
setDictSize(dictSize);
setLcLp(lc, lp);
setPb(pb);
setMode(mode);
setNiceLen(niceLen);
setMatchFinder(mf);
setDepthLimit(depthLimit);
}
/**
* Sets the compression options to the given preset.
* <p>
* The presets 0-3 are fast presets with medium compression. The presets 4-6
* are fairly slow presets with high compression. The default preset
* (<code>PRESET_DEFAULT</code>) is 6.
* <p>
* The presets 7-9 are like the preset 6 but use bigger dictionaries and
* have higher compressor and decompressor memory requirements. Unless the
* uncompressed size of the file exceeds 8&nbsp;MiB, 16&nbsp;MiB, or
* 32&nbsp;MiB, it is waste of memory to use the presets 7, 8, or 9,
* respectively.
*
* @throws UnsupportedOptionsException <code>preset</code> is not supported
*/
public void setPreset(int preset) throws UnsupportedOptionsException {
if (preset < 0 || preset > 9)
throw new UnsupportedOptionsException(
"Unsupported preset: " + preset);
lc = LC_DEFAULT;
lp = LP_DEFAULT;
pb = PB_DEFAULT;
dictSize = presetToDictSize[preset];
if (preset <= 3) {
mode = MODE_FAST;
mf = MF_HC4;
niceLen = preset <= 1 ? 128 : NICE_LEN_MAX;
depthLimit = presetToDepthLimit[preset];
} else {
mode = MODE_NORMAL;
mf = MF_BT4;
niceLen = (preset == 4) ? 16 : (preset == 5) ? 32 : 64;
depthLimit = 0;
}
}
/**
* Sets the dictionary size in bytes.
* <p>
* The dictionary (or history buffer) holds the most recently seen
* uncompressed data. Bigger dictionary usually means better compression.
* However, using a dictioanary bigger than the size of the uncompressed
* data is waste of memory.
* <p>
* Any value in the range [DICT_SIZE_MIN, DICT_SIZE_MAX] is valid, but sizes
* of 2^n and 2^n&nbsp;+&nbsp;2^(n-1) bytes are somewhat recommended.
*
* @throws UnsupportedOptionsException <code>dictSize</code> is not
* supported
*/
public void setDictSize(int dictSize) throws UnsupportedOptionsException {
if (dictSize < DICT_SIZE_MIN)
throw new UnsupportedOptionsException(
"LZMA2 dictionary size must be at least 4 KiB: "
+ dictSize + " B");
if (dictSize > DICT_SIZE_MAX)
throw new UnsupportedOptionsException(
"LZMA2 dictionary size must not exceed "
+ (DICT_SIZE_MAX >> 20) + " MiB: " + dictSize + " B");
this.dictSize = dictSize;
}
/**
* Gets the dictionary size in bytes.
*/
public int getDictSize() {
return dictSize;
}
/**
* Sets a preset dictionary. Use null to disable the use of a preset
* dictionary. By default there is no preset dictionary.
* <p>
* <b>The .xz format doesn't support a preset dictionary for now. Do not set
* a preset dictionary unless you use raw LZMA2.</b>
* <p>
* Preset dictionary can be useful when compressing many similar, relatively
* small chunks of data independently from each other. A preset dictionary
* should contain typical strings that occur in the files being compressed.
* The most probable strings should be near the end of the preset
* dictionary. The preset dictionary used for compression is also needed for
* decompression.
*/
public void setPresetDict(byte[] presetDict) {
this.presetDict = presetDict;
}
/**
* Gets the preset dictionary.
*/
public byte[] getPresetDict() {
return presetDict;
}
/**
* Sets the number of literal context bits and literal position bits.
* <p>
* The sum of <code>lc</code> and <code>lp</code> is limited to 4. Trying to
* exceed it will throw an exception. This function lets you change both at
* the same time.
*
* @throws UnsupportedOptionsException <code>lc</code> and <code>lp</code>
* are invalid
*/
public void setLcLp(int lc, int lp) throws UnsupportedOptionsException {
if (lc < 0 || lp < 0 || lc > LC_LP_MAX || lp > LC_LP_MAX
|| lc + lp > LC_LP_MAX)
throw new UnsupportedOptionsException(
"lc + lp must not exceed " + LC_LP_MAX + ": "
+ lc + " + " + lp);
this.lc = lc;
this.lp = lp;
}
/**
* Sets the number of literal context bits.
* <p>
* All bytes that cannot be encoded as matches are encoded as literals. That
* is, literals are simply 8-bit bytes that are encoded one at a time.
* <p>
* The literal coding makes an assumption that the highest <code>lc</code>
* bits of the previous uncompressed byte correlate with the next byte. For
* example, in typical English text, an upper-case letter is often followed
* by a lower-case letter, and a lower-case letter is usually followed by
* another lower-case letter. In the US-ASCII character set, the highest
* three bits are 010 for upper-case letters and 011 for lower-case letters.
* When <code>lc</code> is at least 3, the literal coding can take advantage
* of this property in the uncompressed data.
* <p>
* The default value (3) is usually good. If you want maximum compression,
* try <code>setLc(4)</code>. Sometimes it helps a little, and sometimes it
* makes compression worse. If it makes it worse, test for example
* <code>setLc(2)</code> too.
*
* @throws UnsupportedOptionsException <code>lc</code> is invalid, or the
* sum of <code>lc</code> and <code>lp</code> exceed LC_LP_MAX
*/
public void setLc(int lc) throws UnsupportedOptionsException {
setLcLp(lc, lp);
}
/**
* Sets the number of literal position bits.
* <p>
* This affets what kind of alignment in the uncompressed data is assumed
* when encoding literals. See {@link #setPb(int) setPb} for more
* information about alignment.
*
* @throws UnsupportedOptionsException <code>lp</code> is invalid, or the
* sum of <code>lc</code> and <code>lp</code> exceed LC_LP_MAX
*/
public void setLp(int lp) throws UnsupportedOptionsException {
setLcLp(lc, lp);
}
/**
* Gets the number of literal context bits.
*/
public int getLc() {
return lc;
}
/**
* Gets the number of literal position bits.
*/
public int getLp() {
return lp;
}
/**
* Sets the number of position bits.
* <p>
* This affects what kind of alignment in the uncompressed data is assumed
* in general. The default (2) means four-byte alignment (2^<code>pb</code>
* = 2^2 = 4), which is often a good choice when there's no better guess.
* <p>
* When the alignment is known, setting the number of position bits
* accordingly may reduce the file size a little. For example with text
* files having one-byte alignment (US-ASCII, ISO-8859-*, UTF-8), using
* <code>setPb(0)</code> can improve compression slightly. For UTF-16 text,
* <code>setPb(1)</code> is a good choice. If the alignment is an odd number
* like 3 bytes, <code>setPb(0)</code> might be the best choice.
* <p>
* Even though the assumed alignment can be adjusted with <code>setPb</code>
* and <code>setLp</code>, LZMA2 still slightly favors 16-byte alignment. It
* might be worth taking into account when designing file formats that are
* likely to be often compressed with LZMA2.
*
* @throws UnsupportedOptionsException <code>pb</code> is invalid
*/
public void setPb(int pb) throws UnsupportedOptionsException {
if (pb < 0 || pb > PB_MAX)
throw new UnsupportedOptionsException(
"pb must not exceed " + PB_MAX + ": " + pb);
this.pb = pb;
}
/**
* Gets the number of position bits.
*/
public int getPb() {
return pb;
}
/**
* Sets the compression mode.
* <p>
* This specifies the method to analyze the data produced by a match finder.
* The default is <code>MODE_FAST</code> for presets 0-3 and
* <code>MODE_NORMAL</code> for presets 4-9.
* <p>
* Usually <code>MODE_FAST</code> is used with Hash Chain match finders and
* <code>MODE_NORMAL</code> with Binary Tree match finders. This is also
* what the presets do.
* <p>
* The special mode <code>MODE_UNCOMPRESSED</code> doesn't try to compress
* the data at all (and doesn't use a match finder) and will simply wrap it
* in uncompressed LZMA2 chunks.
*
* @throws UnsupportedOptionsException <code>mode</code> is not supported
*/
public void setMode(int mode) throws UnsupportedOptionsException {
if (mode < MODE_UNCOMPRESSED || mode > MODE_NORMAL)
throw new UnsupportedOptionsException(
"Unsupported compression mode: " + mode);
this.mode = mode;
}
/**
* Gets the compression mode.
*/
public int getMode() {
return mode;
}
/**
* Sets the nice length of matches. Once a match of at least
* <code>niceLen</code> bytes is found, the algorithm stops looking for
* better matches. Higher values tend to give better compression at the
* expense of speed. The default depends on the preset.
*
* @throws UnsupportedOptionsException <code>niceLen</code> is invalid
*/
public void setNiceLen(int niceLen) throws UnsupportedOptionsException {
if (niceLen < NICE_LEN_MIN)
throw new UnsupportedOptionsException(
"Minimum nice length of matches is "
+ NICE_LEN_MIN + " bytes: " + niceLen);
if (niceLen > NICE_LEN_MAX)
throw new UnsupportedOptionsException(
"Maximum nice length of matches is " + NICE_LEN_MAX
+ ": " + niceLen);
this.niceLen = niceLen;
}
/**
* Gets the nice length of matches.
*/
public int getNiceLen() {
return niceLen;
}
/**
* Sets the match finder type.
* <p>
* Match finder has a major effect on compression speed, memory usage, and
* compression ratio. Usually Hash Chain match finders are faster than
* Binary Tree match finders. The default depends on the preset: 0-3 use
* <code>MF_HC4</code> and 4-9 use <code>MF_BT4</code>.
*
* @throws UnsupportedOptionsException <code>mf</code> is not supported
*/
public void setMatchFinder(int mf) throws UnsupportedOptionsException {
if (mf != MF_HC4 && mf != MF_BT4)
throw new UnsupportedOptionsException(
"Unsupported match finder: " + mf);
this.mf = mf;
}
/**
* Gets the match finder type.
*/
public int getMatchFinder() {
return mf;
}
/**
* Sets the match finder search depth limit.
* <p>
* The default is a special value of <code>0</code> which indicates that the
* depth limit should be automatically calculated by the selected match
* finder from the nice length of matches.
* <p>
* Reasonable depth limit for Hash Chain match finders is 4-100 and 16-1000
* for Binary Tree match finders. Using very high values can make the
* compressor extremely slow with some files. Avoid settings higher than
* 1000 unless you are prepared to interrupt the compression in case it is
* taking far too long.
*
* @throws UnsupportedOptionsException <code>depthLimit</code> is invalid
*/
public void setDepthLimit(int depthLimit)
throws UnsupportedOptionsException {
if (depthLimit < 0)
throw new UnsupportedOptionsException(
"Depth limit cannot be negative: " + depthLimit);
this.depthLimit = depthLimit;
}
/**
* Gets the match finder search depth limit.
*/
public int getDepthLimit() {
return depthLimit;
}
public int getEncoderMemoryUsage() {
return (mode == MODE_UNCOMPRESSED)
? UncompressedLZMA2OutputStream.getMemoryUsage()
: LZMA2OutputStream.getMemoryUsage(this);
}
public FinishableOutputStream getOutputStream(FinishableOutputStream out) {
if (mode == MODE_UNCOMPRESSED)
return new UncompressedLZMA2OutputStream(out);
return new LZMA2OutputStream(out, this);
}
/**
* Gets how much memory the LZMA2 decoder will need to decompress the data
* that was encoded with these options and stored in a .xz file.
* <p>
* The returned value may bigger than the value returned by a direct call to
* {@link LZMA2InputStream#getMemoryUsage(int)} if the dictionary size is
* not 2^n or 2^n&nbsp;+&nbsp;2^(n-1) bytes. This is because the .xz headers
* store the dictionary size in such a format and other values are rounded
* up to the next such value. Such rounding is harmess except it might waste
* some memory if an unsual dictionary size is used.
* <p>
* If you use raw LZMA2 streams and unusual dictioanary size, call
* {@link LZMA2InputStream#getMemoryUsage} directly to get raw decoder
* memory requirements.
*/
public int getDecoderMemoryUsage() {
// Round the dictionary size up to the next 2^n or 2^n + 2^(n-1).
int d = dictSize - 1;
d |= d >>> 2;
d |= d >>> 3;
d |= d >>> 4;
d |= d >>> 8;
d |= d >>> 16;
return LZMA2InputStream.getMemoryUsage(d + 1);
}
public InputStream getInputStream(InputStream in) throws IOException {
return new LZMA2InputStream(in, dictSize);
}
FilterEncoder getFilterEncoder() {
return new LZMA2Encoder(this);
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
assert false;
throw new RuntimeException();
}
}
}

View File

@@ -1,259 +0,0 @@
/*
* LZMA2OutputStream
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.DataOutputStream;
import java.io.IOException;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.rangecoder.RangeEncoder;
import org.tukaani.xz.lzma.LZMAEncoder;
class LZMA2OutputStream extends FinishableOutputStream {
static final int COMPRESSED_SIZE_MAX = 64 << 10;
private FinishableOutputStream out;
private final DataOutputStream outData;
private final LZEncoder lz;
private final RangeEncoder rc;
private final LZMAEncoder lzma;
private final int props; // Cannot change props on the fly for now.
private boolean dictResetNeeded = true;
private boolean stateResetNeeded = true;
private boolean propsNeeded = true;
private int pendingSize = 0;
private boolean finished = false;
private IOException exception = null;
private final byte[] tempBuf = new byte[1];
private static int getExtraSizeBefore(int dictSize) {
return COMPRESSED_SIZE_MAX > dictSize
? COMPRESSED_SIZE_MAX - dictSize : 0;
}
static int getMemoryUsage(LZMA2Options options) {
// 64 KiB buffer for the range encoder + a little extra + LZMAEncoder
int dictSize = options.getDictSize();
int extraSizeBefore = getExtraSizeBefore(dictSize);
return 70 + LZMAEncoder.getMemoryUsage(options.getMode(),
dictSize, extraSizeBefore,
options.getMatchFinder());
}
LZMA2OutputStream(FinishableOutputStream out, LZMA2Options options) {
if (out == null)
throw new NullPointerException();
this.out = out;
outData = new DataOutputStream(out);
rc = new RangeEncoder(COMPRESSED_SIZE_MAX);
int dictSize = options.getDictSize();
int extraSizeBefore = getExtraSizeBefore(dictSize);
lzma = LZMAEncoder.getInstance(rc,
options.getLc(), options.getLp(), options.getPb(),
options.getMode(),
dictSize, extraSizeBefore, options.getNiceLen(),
options.getMatchFinder(), options.getDepthLimit());
lz = lzma.getLZEncoder();
byte[] presetDict = options.getPresetDict();
if (presetDict != null && presetDict.length > 0) {
lz.setPresetDict(dictSize, presetDict);
dictResetNeeded = false;
}
props = (options.getPb() * 5 + options.getLp()) * 9 + options.getLc();
}
public void write(int b) throws IOException {
tempBuf[0] = (byte) b;
write(tempBuf, 0, 1);
}
public void write(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished or closed");
try {
while (len > 0) {
int used = lz.fillWindow(buf, off, len);
off += used;
len -= used;
pendingSize += used;
if (lzma.encodeForLZMA2())
writeChunk();
}
} catch (IOException e) {
exception = e;
throw e;
}
}
private void writeChunk() throws IOException {
int compressedSize = rc.finish();
int uncompressedSize = lzma.getUncompressedSize();
assert compressedSize > 0 : compressedSize;
assert uncompressedSize > 0 : uncompressedSize;
// +2 because the header of a compressed chunk is 2 bytes
// bigger than the header of an uncompressed chunk.
if (compressedSize + 2 < uncompressedSize)
writeLZMA(uncompressedSize, compressedSize);
else {
lzma.reset();
uncompressedSize = lzma.getUncompressedSize();
assert uncompressedSize > 0 : uncompressedSize;
writeUncompressed(uncompressedSize);
}
pendingSize -= uncompressedSize;
lzma.resetUncompressedSize();
rc.reset();
}
private void writeLZMA(int uncompressedSize, int compressedSize)
throws IOException {
int control;
if (propsNeeded)
if (dictResetNeeded)
control = 0x80 + (3 << 5);
else
control = 0x80 + (2 << 5);
else if (stateResetNeeded)
control = 0x80 + (1 << 5);
else
control = 0x80;
control |= (uncompressedSize - 1) >>> 16;
outData.writeByte(control);
outData.writeShort(uncompressedSize - 1);
outData.writeShort(compressedSize - 1);
if (propsNeeded)
outData.writeByte(props);
rc.write(out);
propsNeeded = false;
stateResetNeeded = false;
dictResetNeeded = false;
}
private void writeUncompressed(int uncompressedSize) throws IOException {
while (uncompressedSize > 0) {
int chunkSize = Math.min(uncompressedSize, COMPRESSED_SIZE_MAX);
outData.writeByte(dictResetNeeded ? 0x01 : 0x02);
outData.writeShort(chunkSize - 1);
lz.copyUncompressed(out, uncompressedSize, chunkSize);
uncompressedSize -= chunkSize;
dictResetNeeded = false;
}
stateResetNeeded = true;
}
private void writeEndMarker() throws IOException {
assert !finished;
if (exception != null)
throw exception;
lz.setFinishing();
try {
while (pendingSize > 0) {
lzma.encodeForLZMA2();
writeChunk();
}
out.write(0x00);
} catch (IOException e) {
exception = e;
throw e;
}
finished = true;
}
public void flush() throws IOException {
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished or closed");
try {
lz.setFlushing();
while (pendingSize > 0) {
lzma.encodeForLZMA2();
writeChunk();
}
out.flush();
} catch (IOException e) {
exception = e;
throw e;
}
}
public void finish() throws IOException {
if (!finished) {
writeEndMarker();
try {
out.finish();
} catch (IOException e) {
exception = e;
throw e;
}
finished = true;
}
}
public void close() throws IOException {
if (out != null) {
if (!finished)
try {
writeEndMarker();
} catch (IOException e) {
}
try {
out.close();
} catch (IOException e) {
if (exception == null)
exception = e;
}
out = null;
}
if (exception != null)
throw exception;
}
}

View File

@@ -1,548 +0,0 @@
/*
* LZMAInputStream
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import org.tukaani.xz.lz.LZDecoder;
import org.tukaani.xz.rangecoder.RangeDecoderFromStream;
import org.tukaani.xz.lzma.LZMADecoder;
/**
* Decompresses legacy .lzma files and raw LZMA streams (no .lzma header).
* <p>
* <b>IMPORTANT:</b> In contrast to other classes in this package, this class
* reads data from its input stream one byte at a time. If the input stream is
* for example {@link java.io.FileInputStream}, wrapping it into
* {@link java.io.BufferedInputStream} tends to improve performance a lot. This
* is not automatically done by this class because there may be use cases where
* it is desired that this class won't read any bytes past the end of the LZMA
* stream.
* <p>
* Even when using <code>BufferedInputStream</code>, the performance tends to be
* worse (maybe 10-20&nbsp;% slower) than with {@link LZMA2InputStream} or
* {@link XZInputStream} (when the .xz file contains LZMA2-compressed data).
*
* @since 1.4
*/
public class LZMAInputStream extends InputStream {
/**
* Largest dictionary size supported by this implementation.
* <p>
* LZMA allows dictionaries up to one byte less than 4 GiB. This
* implementation supports only 16 bytes less than 2 GiB. This limitation is
* due to Java using signed 32-bit integers for array indexing. The
* limitation shouldn't matter much in practice since so huge dictionaries
* are not normally used.
*/
public static final int DICT_SIZE_MAX = Integer.MAX_VALUE & ~15;
private InputStream in;
private LZDecoder lz;
private RangeDecoderFromStream rc;
private LZMADecoder lzma;
private boolean endReached = false;
private final byte[] tempBuf = new byte[1];
/**
* Number of uncompressed bytes left to be decompressed, or -1 if the end
* marker is used.
*/
private long remainingSize;
private IOException exception = null;
/**
* Gets approximate decompressor memory requirements as kibibytes for the
* given dictionary size and LZMA properties byte (lc, lp, and pb).
*
* @param dictSize LZMA dictionary size as bytes, should be in the range
* [<code>0</code>, <code>DICT_SIZE_MAX</code>]
*
* @param propsByte LZMA properties byte that encodes the values of lc, lp,
* and pb
*
* @return approximate memory requirements as kibibytes (KiB)
*
* @throws UnsupportedOptionsException if <code>dictSize</code> is outside
* the range [<code>0</code>, <code>DICT_SIZE_MAX</code>]
*
* @throws CorruptedInputException if <code>propsByte</code> is invalid
*/
public static int getMemoryUsage(int dictSize, byte propsByte)
throws UnsupportedOptionsException, CorruptedInputException {
if (dictSize < 0 || dictSize > DICT_SIZE_MAX)
throw new UnsupportedOptionsException(
"LZMA dictionary is too big for this implementation");
int props = propsByte & 0xFF;
if (props > (4 * 5 + 4) * 9 + 8)
throw new CorruptedInputException("Invalid LZMA properties byte");
props %= 9 * 5;
int lp = props / 9;
int lc = props - lp * 9;
return getMemoryUsage(dictSize, lc, lp);
}
/**
* Gets approximate decompressor memory requirements as kibibytes for the
* given dictionary size, lc, and lp. Note that pb isn't needed.
*
* @param dictSize LZMA dictionary size as bytes, must be in the range
* [<code>0</code>, <code>DICT_SIZE_MAX</code>]
*
* @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]
*
* @return approximate memory requirements as kibibytes (KiB)
*/
public static int getMemoryUsage(int dictSize, int lc, int lp) {
if (lc < 0 || lc > 8 || lp < 0 || lp > 4)
throw new IllegalArgumentException("Invalid lc or lp");
// Probability variables have the type "short". There are
// 0x300 (768) probability variables in each literal subcoder.
// The number of literal subcoders is 2^(lc + lp).
//
// Roughly 10 KiB for the base state + LZ decoder's dictionary buffer
// + sizeof(short) * number probability variables per literal subcoder
// * number of literal subcoders
return 10 + getDictSize(dictSize) / 1024
+ ((2 * 0x300) << (lc + lp)) / 1024;
}
private static int getDictSize(int dictSize) {
if (dictSize < 0 || dictSize > DICT_SIZE_MAX)
throw new IllegalArgumentException(
"LZMA dictionary is too big for this implementation");
// For performance reasons, use a 4 KiB dictionary if something
// smaller was requested. It's a rare situation and the performance
// difference isn't huge, and it starts to matter mostly when the
// dictionary is just a few bytes. But we need to handle the special
// case of dictSize == 0 anyway, which is an allowed value but in
// practice means one-byte dictionary.
//
// Note that using a dictionary bigger than specified in the headers
// can hide errors if there is a reference to data beyond the original
// dictionary size but is still within 4 KiB.
if (dictSize < 4096)
dictSize = 4096;
// Round dictionary size upward to a multiple of 16. This way LZMA
// can use LZDecoder.getPos() for calculating LZMA's posMask.
return (dictSize + 15) & ~15;
}
/**
* Creates a new .lzma file format decompressor without a memory usage
* limit.
*
* @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
*
* @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>
*/
public LZMAInputStream(InputStream in) throws IOException {
this(in, -1);
}
/**
* Creates a new .lzma file format decompressor with an optional memory
* usage limit.
*
* @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
*
* @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>
*/
public LZMAInputStream(InputStream in, int memoryLimit)
throws IOException {
DataInputStream inData = new DataInputStream(in);
// Properties byte (lc, lp, and pb)
byte propsByte = inData.readByte();
// Dictionary size is an unsigned 32-bit little endian integer.
int dictSize = 0;
for (int i = 0; i < 4; ++i)
dictSize |= inData.readUnsignedByte() << (8 * i);
// Uncompressed size is an unsigned 64-bit little endian integer.
// The maximum 64-bit value is a special case (becomes -1 here)
// which indicates that the end marker is used instead of knowing
// the uncompressed size beforehand.
long uncompSize = 0;
for (int i = 0; i < 8; ++i)
uncompSize |= (long) inData.readUnsignedByte() << (8 * i);
// Check the memory usage limit.
int memoryNeeded = getMemoryUsage(dictSize, propsByte);
if (memoryLimit != -1 && memoryNeeded > memoryLimit)
throw new MemoryLimitException(memoryNeeded, memoryLimit);
initialize(in, uncompSize, propsByte, dictSize, null);
}
/**
* Creates a new input stream that decompresses raw LZMA data (no .lzma
* header) from <code>in</code>.
* <p>
* The caller needs to know if the "end of payload marker (EOPM)" alias "end
* of stream marker (EOS marker)" alias "end marker" present. If the end
* marker isn't used, the caller must know the exact uncompressed size of
* the stream.
* <p>
* The caller also needs to provide the LZMA properties byte that encodes
* the number of literal context bits (lc), literal position bits (lp), and
* position bits (pb).
* <p>
* The dictionary size used when compressing is also needed. Specifying a
* too small dictionary size will prevent decompressing the stream.
* Specifying a too big dictionary is waste of memory but decompression will
* work.
* <p>
* There is no need to specify a dictionary bigger than the uncompressed
* size of the data even if a bigger dictionary was used when compressing.
* If you know the uncompressed size of the data, this might allow saving
* some memory.
*
* @param in input stream from which 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>]
*
* @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
*
*
*/
public LZMAInputStream(InputStream in, long uncompSize, byte propsByte,
int dictSize) throws IOException {
initialize(in, uncompSize, propsByte, dictSize, null);
}
/**
* Creates a new input stream that decompresses raw LZMA data (no .lzma
* header) from <code>in</code> optionally with a preset dictionary.
*
* @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
*
* @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>
*/
public LZMAInputStream(InputStream in, long uncompSize, byte propsByte,
int dictSize, byte[] presetDict)
throws IOException {
initialize(in, uncompSize, propsByte, dictSize, presetDict);
}
/**
* Creates a new input stream that decompresses raw LZMA data (no .lzma
* header) from <code>in</code> optionally with a preset dictionary.
*
* @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
*
* @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>
*/
public LZMAInputStream(InputStream in, long uncompSize,
int lc, int lp, int pb,
int dictSize, byte[] presetDict)
throws IOException {
initialize(in, uncompSize, lc, lp, pb, dictSize, presetDict);
}
private void initialize(InputStream in, long uncompSize, byte propsByte,
int dictSize, byte[] presetDict)
throws IOException {
// Validate the uncompressed size since the other "initialize" throws
// IllegalArgumentException if uncompSize < -1.
if (uncompSize < -1)
throw new UnsupportedOptionsException(
"Uncompressed size is too big");
// Decode the properties byte. In contrast to LZMA2, there is no
// limit of lc + lp <= 4.
int props = propsByte & 0xFF;
if (props > (4 * 5 + 4) * 9 + 8)
throw new CorruptedInputException("Invalid LZMA properties byte");
int pb = props / (9 * 5);
props -= pb * 9 * 5;
int lp = props / 9;
int lc = props - lp * 9;
// Validate the dictionary size since the other "initialize" throws
// IllegalArgumentException if dictSize is not supported.
if (dictSize < 0 || dictSize > DICT_SIZE_MAX)
throw new UnsupportedOptionsException(
"LZMA dictionary is too big for this implementation");
initialize(in, uncompSize, lc, lp, pb, dictSize, presetDict);
}
private void initialize(InputStream in, long uncompSize,
int lc, int lp, int pb,
int dictSize, byte[] presetDict)
throws IOException {
// getDictSize validates dictSize and gives a message in
// the exception too, so skip validating dictSize here.
if (uncompSize < -1 || lc < 0 || lc > 8 || lp < 0 || lp > 4
|| pb < 0 || pb > 4)
throw new IllegalArgumentException();
this.in = in;
// If uncompressed size is known, use it to avoid wasting memory for
// a uselessly large dictionary buffer.
dictSize = getDictSize(dictSize);
if (uncompSize >= 0 && dictSize > uncompSize)
dictSize = getDictSize((int) uncompSize);
lz = new LZDecoder(getDictSize(dictSize), presetDict);
rc = new RangeDecoderFromStream(in);
lzma = new LZMADecoder(lz, rc, lc, lp, pb);
remainingSize = uncompSize;
}
/**
* Decompresses the next byte from this input stream.
* <p>
* Reading lots of data with <code>read()</code> from this input stream may
* be inefficient. Wrap it in <code>java.io.BufferedInputStream</code> if
* you need to read lots of data one byte at a time.
*
* @return the next decompressed byte, or <code>-1</code> to indicate the
* end of the compressed stream
*
* @throws CorruptedInputException
*
* @throws XZIOException if the stream has been closed
*
* @throws EOFException compressed input is truncated or corrupt
*
* @throws IOException may be thrown by <code>in</code>
*/
public int read() throws IOException {
return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF);
}
/**
* Decompresses into an array of bytes.
* <p>
* If <code>len</code> is zero, no bytes are read and <code>0</code> is
* returned. Otherwise this will block until <code>len</code> bytes have
* been decompressed, the end of the LZMA stream is reached, or an exception
* is thrown.
*
* @param buf target buffer for uncompressed data
* @param off start offset in <code>buf</code>
* @param len maximum number of uncompressed bytes to read
*
* @return number of bytes read, or <code>-1</code> to indicate the end of
* the compressed stream
*
* @throws CorruptedInputException
*
* @throws XZIOException if the stream has been closed
*
* @throws EOFException compressed input is truncated or corrupt
*
* @throws IOException may be thrown by <code>in</code>
*/
public int read(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
if (len == 0)
return 0;
if (in == null)
throw new XZIOException("Stream closed");
if (exception != null)
throw exception;
if (endReached)
return -1;
try {
int size = 0;
while (len > 0) {
// If uncompressed size is known and thus no end marker will
// be present, set the limit so that the uncompressed size
// won't be exceeded.
int copySizeMax = len;
if (remainingSize >= 0 && remainingSize < len)
copySizeMax = (int) remainingSize;
lz.setLimit(copySizeMax);
// Decode into the dictionary buffer.
try {
lzma.decode();
} catch (CorruptedInputException e) {
// The end marker is encoded with a LZMA symbol that
// indicates maximum match distance. This is larger
// than any supported dictionary and thus causes
// CorruptedInputException from LZDecoder.repeat.
if (remainingSize != -1 || !lzma.endMarkerDetected())
throw e;
endReached = true;
// The exception makes lzma.decode() miss the last range
// decoder normalization, so do it here. This might
// cause an IOException if it needs to read a byte
// from the input stream.
rc.normalize();
}
// Copy from the dictionary to buf.
int copiedSize = lz.flush(buf, off);
off += copiedSize;
len -= copiedSize;
size += copiedSize;
if (remainingSize >= 0) {
// Update the number of bytes left to be decompressed.
remainingSize -= copiedSize;
assert remainingSize >= 0;
if (remainingSize == 0)
endReached = true;
}
if (endReached) {
// Checking these helps a lot when catching corrupt
// or truncated .lzma files. LZMA Utils doesn't do
// the first check and thus it accepts many invalid
// files that this implementation and XZ Utils don't.
if (!rc.isFinished() || lz.hasPending())
throw new CorruptedInputException();
return size == 0 ? -1 : size;
}
}
return size;
} catch (IOException e) {
exception = e;
throw e;
}
}
/**
* Closes the stream and calls <code>in.close()</code>. If the stream was
* already closed, this does nothing.
*
* @throws IOException if thrown by <code>in.close()</code>
*/
public void close() throws IOException {
if (in != null)
try {
in.close();
} finally {
in = null;
}
}
}

View File

@@ -1,153 +0,0 @@
/*
* UncompressedLZMA2OutputStream
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.DataOutputStream;
import java.io.IOException;
class UncompressedLZMA2OutputStream extends FinishableOutputStream {
private FinishableOutputStream out;
private final DataOutputStream outData;
private final byte[] uncompBuf
= new byte[LZMA2OutputStream.COMPRESSED_SIZE_MAX];
private int uncompPos = 0;
private boolean dictResetNeeded = true;
private boolean finished = false;
private IOException exception = null;
private final byte[] tempBuf = new byte[1];
static int getMemoryUsage() {
// uncompBuf + a little extra
return 70;
}
UncompressedLZMA2OutputStream(FinishableOutputStream out) {
if (out == null)
throw new NullPointerException();
this.out = out;
outData = new DataOutputStream(out);
}
public void write(int b) throws IOException {
tempBuf[0] = (byte) b;
write(tempBuf, 0, 1);
}
public void write(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished or closed");
try {
while (len > 0) {
int copySize = Math.min(uncompBuf.length - uncompPos, len);
System.arraycopy(buf, off, uncompBuf, uncompPos, copySize);
len -= copySize;
uncompPos += copySize;
if (uncompPos == uncompBuf.length)
writeChunk();
}
} catch (IOException e) {
exception = e;
throw e;
}
}
private void writeChunk() throws IOException {
outData.writeByte(dictResetNeeded ? 0x01 : 0x02);
outData.writeShort(uncompPos - 1);
outData.write(uncompBuf, 0, uncompPos);
uncompPos = 0;
dictResetNeeded = false;
}
private void writeEndMarker() throws IOException {
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished or closed");
try {
if (uncompPos > 0)
writeChunk();
out.write(0x00);
} catch (IOException e) {
exception = e;
throw e;
}
}
public void flush() throws IOException {
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished or closed");
try {
if (uncompPos > 0)
writeChunk();
out.flush();
} catch (IOException e) {
exception = e;
throw e;
}
}
public void finish() throws IOException {
if (!finished) {
writeEndMarker();
try {
out.finish();
} catch (IOException e) {
exception = e;
throw e;
}
finished = true;
}
}
public void close() throws IOException {
if (out != null) {
if (!finished)
try {
writeEndMarker();
} catch (IOException e) {
}
try {
out.close();
} catch (IOException e) {
if (exception == null)
exception = e;
}
out = null;
}
if (exception != null)
throw exception;
}
}

View File

@@ -1,27 +0,0 @@
/*
* DeltaCoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.delta;
abstract class DeltaCoder {
static final int DISTANCE_MIN = 1;
static final int DISTANCE_MAX = 256;
static final int DISTANCE_MASK = DISTANCE_MAX - 1;
final int distance;
final byte[] history = new byte[DISTANCE_MAX];
int pos = 0;
DeltaCoder(int distance) {
if (distance < DISTANCE_MIN || distance > DISTANCE_MAX)
throw new IllegalArgumentException();
this.distance = distance;
}
}

View File

@@ -1,24 +0,0 @@
/*
* DeltaDecoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.delta;
public class DeltaDecoder extends DeltaCoder {
public DeltaDecoder(int distance) {
super(distance);
}
public void decode(byte[] buf, int off, int len) {
int end = off + len;
for (int i = off; i < end; ++i) {
buf[i] += history[(distance + pos) & DISTANCE_MASK];
history[pos-- & DISTANCE_MASK] = buf[i];
}
}
}

View File

@@ -1,24 +0,0 @@
/*
* DeltaEncoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.delta;
public class DeltaEncoder extends DeltaCoder {
public DeltaEncoder(int distance) {
super(distance);
}
public void encode(byte[] in, int in_off, int len, byte[] out) {
for (int i = 0; i < len; ++i) {
byte tmp = history[(distance + pos) & DISTANCE_MASK];
history[pos-- & DISTANCE_MASK] = in[in_off + i];
out[i] = (byte) (in[in_off + i] - tmp);
}
}
}

View File

@@ -1,140 +0,0 @@
/*
* LZMACoder
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.lzma;
import org.tukaani.xz.rangecoder.RangeCoder;
abstract class LZMACoder {
static final int POS_STATES_MAX = 1 << 4;
static final int MATCH_LEN_MIN = 2;
static final int MATCH_LEN_MAX = MATCH_LEN_MIN + LengthCoder.LOW_SYMBOLS
+ LengthCoder.MID_SYMBOLS
+ LengthCoder.HIGH_SYMBOLS - 1;
static final int DIST_STATES = 4;
static final int DIST_SLOTS = 1 << 6;
static final int DIST_MODEL_START = 4;
static final int DIST_MODEL_END = 14;
static final int FULL_DISTANCES = 1 << (DIST_MODEL_END / 2);
static final int ALIGN_BITS = 4;
static final int ALIGN_SIZE = 1 << ALIGN_BITS;
static final int ALIGN_MASK = ALIGN_SIZE - 1;
static final int REPS = 4;
final int posMask;
final int[] reps = new int[REPS];
final State state = new State();
final short[][] isMatch = new short[State.STATES][POS_STATES_MAX];
final short[] isRep = new short[State.STATES];
final short[] isRep0 = new short[State.STATES];
final short[] isRep1 = new short[State.STATES];
final short[] isRep2 = new short[State.STATES];
final short[][] isRep0Long = new short[State.STATES][POS_STATES_MAX];
final short[][] distSlots = new short[DIST_STATES][DIST_SLOTS];
final short[][] distSpecial = { new short[2], new short[2],
new short[4], new short[4],
new short[8], new short[8],
new short[16], new short[16],
new short[32], new short[32] };
final short[] distAlign = new short[ALIGN_SIZE];
static final int getDistState(int len) {
return len < DIST_STATES + MATCH_LEN_MIN
? len - MATCH_LEN_MIN
: DIST_STATES - 1;
}
LZMACoder(int pb) {
posMask = (1 << pb) - 1;
}
void reset() {
reps[0] = 0;
reps[1] = 0;
reps[2] = 0;
reps[3] = 0;
state.reset();
for (int i = 0; i < isMatch.length; ++i)
RangeCoder.initProbs(isMatch[i]);
RangeCoder.initProbs(isRep);
RangeCoder.initProbs(isRep0);
RangeCoder.initProbs(isRep1);
RangeCoder.initProbs(isRep2);
for (int i = 0; i < isRep0Long.length; ++i)
RangeCoder.initProbs(isRep0Long[i]);
for (int i = 0; i < distSlots.length; ++i)
RangeCoder.initProbs(distSlots[i]);
for (int i = 0; i < distSpecial.length; ++i)
RangeCoder.initProbs(distSpecial[i]);
RangeCoder.initProbs(distAlign);
}
abstract class LiteralCoder {
private final int lc;
private final int literalPosMask;
LiteralCoder(int lc, int lp) {
this.lc = lc;
this.literalPosMask = (1 << lp) - 1;
}
final int getSubcoderIndex(int prevByte, int pos) {
int low = prevByte >> (8 - lc);
int high = (pos & literalPosMask) << lc;
return low + high;
}
abstract class LiteralSubcoder {
final short[] probs = new short[0x300];
void reset() {
RangeCoder.initProbs(probs);
}
}
}
abstract class LengthCoder {
static final int LOW_SYMBOLS = 1 << 3;
static final int MID_SYMBOLS = 1 << 3;
static final int HIGH_SYMBOLS = 1 << 8;
final short[] choice = new short[2];
final short[][] low = new short[POS_STATES_MAX][LOW_SYMBOLS];
final short[][] mid = new short[POS_STATES_MAX][MID_SYMBOLS];
final short[] high = new short[HIGH_SYMBOLS];
void reset() {
RangeCoder.initProbs(choice);
for (int i = 0; i < low.length; ++i)
RangeCoder.initProbs(low[i]);
for (int i = 0; i < low.length; ++i)
RangeCoder.initProbs(mid[i]);
RangeCoder.initProbs(high);
}
}
}

View File

@@ -1,198 +0,0 @@
/*
* LZMADecoder
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.lzma;
import java.io.IOException;
import org.tukaani.xz.lz.LZDecoder;
import org.tukaani.xz.rangecoder.RangeDecoder;
public final class LZMADecoder extends LZMACoder {
private final LZDecoder lz;
private final RangeDecoder rc;
private final LiteralDecoder literalDecoder;
private final LengthDecoder matchLenDecoder = new LengthDecoder();
private final LengthDecoder repLenDecoder = new LengthDecoder();
public LZMADecoder(LZDecoder lz, RangeDecoder rc, int lc, int lp, int pb) {
super(pb);
this.lz = lz;
this.rc = rc;
this.literalDecoder = new LiteralDecoder(lc, lp);
reset();
}
public void reset() {
super.reset();
literalDecoder.reset();
matchLenDecoder.reset();
repLenDecoder.reset();
}
/**
* Returns true if LZMA end marker was detected. It is encoded as the
* maximum match distance which with signed ints becomes -1. This function
* is needed only for LZMA1. LZMA2 doesn't use the end marker in the LZMA
* layer.
*/
public boolean endMarkerDetected() {
return reps[0] == -1;
}
public void decode() throws IOException {
lz.repeatPending();
while (lz.hasSpace()) {
int posState = lz.getPos() & posMask;
if (rc.decodeBit(isMatch[state.get()], posState) == 0)
literalDecoder.decode();
else {
int len = rc.decodeBit(isRep, state.get()) == 0
? decodeMatch(posState)
: decodeRepMatch(posState);
// NOTE: With LZMA1 streams that have the end marker,
// this will throw CorruptedInputException. LZMAInputStream
// handles it specially.
lz.repeat(reps[0], len);
}
}
rc.normalize();
}
private int decodeMatch(int posState) throws IOException {
state.updateMatch();
reps[3] = reps[2];
reps[2] = reps[1];
reps[1] = reps[0];
int len = matchLenDecoder.decode(posState);
int distSlot = rc.decodeBitTree(distSlots[getDistState(len)]);
if (distSlot < DIST_MODEL_START)
reps[0] = distSlot;
else {
int limit = (distSlot >> 1) - 1;
reps[0] = (2 | (distSlot & 1)) << limit;
if (distSlot < DIST_MODEL_END)
reps[0] |= rc.decodeReverseBitTree(
distSpecial[distSlot - DIST_MODEL_START]);
else {
reps[0] |= rc.decodeDirectBits(limit - ALIGN_BITS)
<< ALIGN_BITS;
reps[0] |= rc.decodeReverseBitTree(distAlign);
}
}
return len;
}
private int decodeRepMatch(int posState) throws IOException {
if (rc.decodeBit(isRep0, state.get()) == 0) {
if (rc.decodeBit(isRep0Long[state.get()], posState) == 0) {
state.updateShortRep();
return 1;
}
} else {
int tmp;
if (rc.decodeBit(isRep1, state.get()) == 0)
tmp = reps[1];
else {
if (rc.decodeBit(isRep2, state.get()) == 0)
tmp = reps[2];
else {
tmp = reps[3];
reps[3] = reps[2];
}
reps[2] = reps[1];
}
reps[1] = reps[0];
reps[0] = tmp;
}
state.updateLongRep();
return repLenDecoder.decode(posState);
}
private class LiteralDecoder extends LiteralCoder {
LiteralSubdecoder[] subdecoders;
LiteralDecoder(int lc, int lp) {
super(lc, lp);
subdecoders = new LiteralSubdecoder[1 << (lc + lp)];
for (int i = 0; i < subdecoders.length; ++i)
subdecoders[i] = new LiteralSubdecoder();
}
void reset() {
for (int i = 0; i < subdecoders.length; ++i)
subdecoders[i].reset();
}
void decode() throws IOException {
int i = getSubcoderIndex(lz.getByte(0), lz.getPos());
subdecoders[i].decode();
}
private class LiteralSubdecoder extends LiteralSubcoder {
void decode() throws IOException {
int symbol = 1;
if (state.isLiteral())
do
symbol = (symbol << 1) | rc.decodeBit(probs, symbol);
while (symbol < 0x100);
else {
int matchByte = lz.getByte(reps[0]);
int offset = 0x100;
int matchBit;
int bit;
do {
matchByte <<= 1;
matchBit = matchByte & offset;
bit = rc.decodeBit(probs, offset + matchBit + symbol);
symbol = (symbol << 1) | bit;
offset &= (0 - bit) ^ ~matchBit;
} while (symbol < 0x100);
}
lz.putByte((byte) symbol);
state.updateLiteral();
}
}
}
private class LengthDecoder extends LengthCoder {
int decode(int posState) throws IOException {
if (rc.decodeBit(choice, 0) == 0)
return rc.decodeBitTree(low[posState]) + MATCH_LEN_MIN;
if (rc.decodeBit(choice, 1) == 0)
return rc.decodeBitTree(mid[posState])
+ MATCH_LEN_MIN + LOW_SYMBOLS;
return rc.decodeBitTree(high)
+ MATCH_LEN_MIN + LOW_SYMBOLS + MID_SYMBOLS;
}
}
}

View File

@@ -1,710 +0,0 @@
/*
* LZMAEncoder
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.lzma;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.lz.Matches;
import org.tukaani.xz.rangecoder.RangeEncoder;
public abstract class LZMAEncoder extends LZMACoder {
public static final int MODE_FAST = 1;
public static final int MODE_NORMAL = 2;
/**
* LZMA2 chunk is considered full when its uncompressed size exceeds
* <code>LZMA2_UNCOMPRESSED_LIMIT</code>.
* <p>
* A compressed LZMA2 chunk can hold 2 MiB of uncompressed data. A single
* LZMA symbol may indicate up to MATCH_LEN_MAX bytes of data, so the LZMA2
* chunk is considered full when there is less space than MATCH_LEN_MAX
* bytes.
*/
private static final int LZMA2_UNCOMPRESSED_LIMIT
= (2 << 20) - MATCH_LEN_MAX;
/**
* LZMA2 chunk is considered full when its compressed size exceeds
* <code>LZMA2_COMPRESSED_LIMIT</code>.
* <p>
* The maximum compressed size of a LZMA2 chunk is 64 KiB. A single LZMA
* symbol might use 20 bytes of space even though it usually takes just one
* byte or so. Two more bytes are needed for LZMA2 uncompressed chunks (see
* LZMA2OutputStream.writeChunk). Leave a little safety margin and use 26
* bytes.
*/
private static final int LZMA2_COMPRESSED_LIMIT = (64 << 10) - 26;
private static final int DIST_PRICE_UPDATE_INTERVAL = FULL_DISTANCES;
private static final int ALIGN_PRICE_UPDATE_INTERVAL = ALIGN_SIZE;
private final RangeEncoder rc;
final LZEncoder lz;
final LiteralEncoder literalEncoder;
final LengthEncoder matchLenEncoder;
final LengthEncoder repLenEncoder;
final int niceLen;
private int distPriceCount = 0;
private int alignPriceCount = 0;
private final int distSlotPricesSize;
private final int[][] distSlotPrices;
private final int[][] fullDistPrices
= new int[DIST_STATES][FULL_DISTANCES];
private final int[] alignPrices = new int[ALIGN_SIZE];
int back = 0;
int readAhead = -1;
private int uncompressedSize = 0;
public static int getMemoryUsage(int mode, int dictSize,
int extraSizeBefore, int mf) {
int m = 80;
switch (mode) {
case MODE_FAST:
m += LZMAEncoderFast.getMemoryUsage(
dictSize, extraSizeBefore, mf);
break;
case MODE_NORMAL:
m += LZMAEncoderNormal.getMemoryUsage(
dictSize, extraSizeBefore, mf);
break;
default:
throw new IllegalArgumentException();
}
return m;
}
public static LZMAEncoder getInstance(
RangeEncoder rc, int lc, int lp, int pb, int mode,
int dictSize, int extraSizeBefore,
int niceLen, int mf, int depthLimit) {
switch (mode) {
case MODE_FAST:
return new LZMAEncoderFast(rc, lc, lp, pb,
dictSize, extraSizeBefore,
niceLen, mf, depthLimit);
case MODE_NORMAL:
return new LZMAEncoderNormal(rc, lc, lp, pb,
dictSize, extraSizeBefore,
niceLen, mf, depthLimit);
}
throw new IllegalArgumentException();
}
/**
* 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 cares about
* the second highest bit.
*/
public static int getDistSlot(int dist) {
if (dist <= DIST_MODEL_START)
return dist;
int n = dist;
int i = 31;
if ((n & 0xFFFF0000) == 0) {
n <<= 16;
i = 15;
}
if ((n & 0xFF000000) == 0) {
n <<= 8;
i -= 8;
}
if ((n & 0xF0000000) == 0) {
n <<= 4;
i -= 4;
}
if ((n & 0xC0000000) == 0) {
n <<= 2;
i -= 2;
}
if ((n & 0x80000000) == 0)
--i;
return (i << 1) + ((dist >>> (i - 1)) & 1);
}
/**
* Gets the next LZMA symbol.
* <p>
* There are three types of symbols: literal (a single byte), repeated
* match, and normal match. The symbol is indicated by the return value and
* by the variable <code>back</code>.
* <p>
* Literal: <code>back == -1</code> and return value is <code>1</code>. The
* literal itself needs to be read from <code>lz</code> separately.
* <p>
* Repeated match: <code>back</code> is in the range [0, 3] and the return
* value is the length of the repeated match.
* <p>
* Normal match: <code>back - REPS<code> (<code>back - 4</code>) is the
* distance of the match and the return value is the length of the match.
*/
abstract int getNextSymbol();
LZMAEncoder(RangeEncoder rc, LZEncoder lz,
int lc, int lp, int pb, int dictSize, int niceLen) {
super(pb);
this.rc = rc;
this.lz = lz;
this.niceLen = niceLen;
literalEncoder = new LiteralEncoder(lc, lp);
matchLenEncoder = new LengthEncoder(pb, niceLen);
repLenEncoder = new LengthEncoder(pb, niceLen);
distSlotPricesSize = getDistSlot(dictSize - 1) + 1;
distSlotPrices = new int[DIST_STATES][distSlotPricesSize];
reset();
}
public LZEncoder getLZEncoder() {
return lz;
}
public void reset() {
super.reset();
literalEncoder.reset();
matchLenEncoder.reset();
repLenEncoder.reset();
distPriceCount = 0;
alignPriceCount = 0;
uncompressedSize += readAhead + 1;
readAhead = -1;
}
public int getUncompressedSize() {
return uncompressedSize;
}
public void resetUncompressedSize() {
uncompressedSize = 0;
}
/**
* Compresses for LZMA2.
*
* @return true if the LZMA2 chunk became full, false otherwise
*/
public boolean encodeForLZMA2() {
if (!lz.isStarted() && !encodeInit())
return false;
while (uncompressedSize <= LZMA2_UNCOMPRESSED_LIMIT
&& rc.getPendingSize() <= LZMA2_COMPRESSED_LIMIT)
if (!encodeSymbol())
return false;
return true;
}
private boolean encodeInit() {
assert readAhead == -1;
if (!lz.hasEnoughData(0))
return false;
// The first symbol must be a literal unless using
// a preset dictionary. This code isn't run if using
// a preset dictionary.
skip(1);
rc.encodeBit(isMatch[state.get()], 0, 0);
literalEncoder.encodeInit();
--readAhead;
assert readAhead == -1;
++uncompressedSize;
assert uncompressedSize == 1;
return true;
}
private boolean encodeSymbol() {
if (!lz.hasEnoughData(readAhead + 1))
return false;
int len = getNextSymbol();
assert readAhead >= 0;
int posState = (lz.getPos() - readAhead) & posMask;
if (back == -1) {
// Literal i.e. eight-bit byte
assert len == 1;
rc.encodeBit(isMatch[state.get()], posState, 0);
literalEncoder.encode();
} else {
// Some type of match
rc.encodeBit(isMatch[state.get()], posState, 1);
if (back < REPS) {
// Repeated match i.e. the same distance
// has been used earlier.
assert lz.getMatchLen(-readAhead, reps[back], len) == len;
rc.encodeBit(isRep, state.get(), 1);
encodeRepMatch(back, len, posState);
} else {
// Normal match
assert lz.getMatchLen(-readAhead, back - REPS, len) == len;
rc.encodeBit(isRep, state.get(), 0);
encodeMatch(back - REPS, len, posState);
}
}
readAhead -= len;
uncompressedSize += len;
return true;
}
private void encodeMatch(int dist, int len, int posState) {
state.updateMatch();
matchLenEncoder.encode(len, posState);
int distSlot = getDistSlot(dist);
rc.encodeBitTree(distSlots[getDistState(len)], distSlot);
if (distSlot >= DIST_MODEL_START) {
int footerBits = (distSlot >>> 1) - 1;
int base = (2 | (distSlot & 1)) << footerBits;
int distReduced = dist - base;
if (distSlot < DIST_MODEL_END)
rc.encodeReverseBitTree(
distSpecial[distSlot - DIST_MODEL_START],
distReduced);
else {
rc.encodeDirectBits(distReduced >>> ALIGN_BITS,
footerBits - ALIGN_BITS);
rc.encodeReverseBitTree(distAlign, distReduced & ALIGN_MASK);
--alignPriceCount;
}
}
reps[3] = reps[2];
reps[2] = reps[1];
reps[1] = reps[0];
reps[0] = dist;
--distPriceCount;
}
private void encodeRepMatch(int rep, int len, int posState) {
if (rep == 0) {
rc.encodeBit(isRep0, state.get(), 0);
rc.encodeBit(isRep0Long[state.get()], posState, len == 1 ? 0 : 1);
} else {
int dist = reps[rep];
rc.encodeBit(isRep0, state.get(), 1);
if (rep == 1)
rc.encodeBit(isRep1, state.get(), 0);
else {
rc.encodeBit(isRep1, state.get(), 1);
rc.encodeBit(isRep2, state.get(), rep - 2);
if (rep == 3)
reps[3] = reps[2];
reps[2] = reps[1];
}
reps[1] = reps[0];
reps[0] = dist;
}
if (len == 1)
state.updateShortRep();
else {
repLenEncoder.encode(len, posState);
state.updateLongRep();
}
}
Matches getMatches() {
++readAhead;
Matches matches = lz.getMatches();
assert lz.verifyMatches(matches);
return matches;
}
void skip(int len) {
readAhead += len;
lz.skip(len);
}
int getAnyMatchPrice(State state, int posState) {
return RangeEncoder.getBitPrice(isMatch[state.get()][posState], 1);
}
int getNormalMatchPrice(int anyMatchPrice, State state) {
return anyMatchPrice
+ RangeEncoder.getBitPrice(isRep[state.get()], 0);
}
int getAnyRepPrice(int anyMatchPrice, State state) {
return anyMatchPrice
+ RangeEncoder.getBitPrice(isRep[state.get()], 1);
}
int getShortRepPrice(int anyRepPrice, State state, int posState) {
return anyRepPrice
+ RangeEncoder.getBitPrice(isRep0[state.get()], 0)
+ RangeEncoder.getBitPrice(isRep0Long[state.get()][posState],
0);
}
int getLongRepPrice(int anyRepPrice, int rep, State state, int posState) {
int price = anyRepPrice;
if (rep == 0)
price += RangeEncoder.getBitPrice(isRep0[state.get()], 0)
+ RangeEncoder.getBitPrice(
isRep0Long[state.get()][posState], 1);
else {
price += RangeEncoder.getBitPrice(isRep0[state.get()], 1);
if (rep == 1)
price += RangeEncoder.getBitPrice(isRep1[state.get()], 0);
else
price += RangeEncoder.getBitPrice(isRep1[state.get()], 1)
+ RangeEncoder.getBitPrice(isRep2[state.get()],
rep - 2);
}
return price;
}
int getLongRepAndLenPrice(int rep, int len, State state, int posState) {
int anyMatchPrice = getAnyMatchPrice(state, posState);
int anyRepPrice = getAnyRepPrice(anyMatchPrice, state);
int longRepPrice = getLongRepPrice(anyRepPrice, rep, state, posState);
return longRepPrice + repLenEncoder.getPrice(len, posState);
}
int getMatchAndLenPrice(int normalMatchPrice,
int dist, int len, int posState) {
int price = normalMatchPrice
+ matchLenEncoder.getPrice(len, posState);
int distState = getDistState(len);
if (dist < FULL_DISTANCES)
price += fullDistPrices[distState][dist];
else {
// Note that distSlotPrices includes also
// the price of direct bits.
int distSlot = getDistSlot(dist);
price += distSlotPrices[distState][distSlot]
+ alignPrices[dist & ALIGN_MASK];
}
return price;
}
private void updateDistPrices() {
distPriceCount = DIST_PRICE_UPDATE_INTERVAL;
for (int distState = 0; distState < DIST_STATES; ++distState) {
for (int distSlot = 0; distSlot < distSlotPricesSize; ++distSlot)
distSlotPrices[distState][distSlot]
= RangeEncoder.getBitTreePrice(
distSlots[distState], distSlot);
for (int distSlot = DIST_MODEL_END; distSlot < distSlotPricesSize;
++distSlot) {
int count = (distSlot >>> 1) - 1 - ALIGN_BITS;
distSlotPrices[distState][distSlot]
+= RangeEncoder.getDirectBitsPrice(count);
}
for (int dist = 0; dist < DIST_MODEL_START; ++dist)
fullDistPrices[distState][dist]
= distSlotPrices[distState][dist];
}
int dist = DIST_MODEL_START;
for (int distSlot = DIST_MODEL_START; distSlot < DIST_MODEL_END;
++distSlot) {
int footerBits = (distSlot >>> 1) - 1;
int base = (2 | (distSlot & 1)) << footerBits;
int limit = distSpecial[distSlot - DIST_MODEL_START].length;
for (int i = 0; i < limit; ++i) {
int distReduced = dist - base;
int price = RangeEncoder.getReverseBitTreePrice(
distSpecial[distSlot - DIST_MODEL_START],
distReduced);
for (int distState = 0; distState < DIST_STATES; ++distState)
fullDistPrices[distState][dist]
= distSlotPrices[distState][distSlot] + price;
++dist;
}
}
assert dist == FULL_DISTANCES;
}
private void updateAlignPrices() {
alignPriceCount = ALIGN_PRICE_UPDATE_INTERVAL;
for (int i = 0; i < ALIGN_SIZE; ++i)
alignPrices[i] = RangeEncoder.getReverseBitTreePrice(distAlign,
i);
}
/**
* Updates the lookup tables used for calculating match distance and length
* prices. The updating is skipped for performance reasons if the tables
* haven't changed much since the previous update.
*/
void updatePrices() {
if (distPriceCount <= 0)
updateDistPrices();
if (alignPriceCount <= 0)
updateAlignPrices();
matchLenEncoder.updatePrices();
repLenEncoder.updatePrices();
}
class LiteralEncoder extends LiteralCoder {
LiteralSubencoder[] subencoders;
LiteralEncoder(int lc, int lp) {
super(lc, lp);
subencoders = new LiteralSubencoder[1 << (lc + lp)];
for (int i = 0; i < subencoders.length; ++i)
subencoders[i] = new LiteralSubencoder();
}
void reset() {
for (int i = 0; i < subencoders.length; ++i)
subencoders[i].reset();
}
void encodeInit() {
// When encoding the first byte of the stream, there is
// no previous byte in the dictionary so the encode function
// wouldn't work.
assert readAhead >= 0;
subencoders[0].encode();
}
void encode() {
assert readAhead >= 0;
int i = getSubcoderIndex(lz.getByte(1 + readAhead),
lz.getPos() - readAhead);
subencoders[i].encode();
}
int getPrice(int curByte, int matchByte,
int prevByte, int pos, State state) {
int price = RangeEncoder.getBitPrice(
isMatch[state.get()][pos & posMask], 0);
int i = getSubcoderIndex(prevByte, pos);
price += state.isLiteral()
? subencoders[i].getNormalPrice(curByte)
: subencoders[i].getMatchedPrice(curByte, matchByte);
return price;
}
private class LiteralSubencoder extends LiteralSubcoder {
void encode() {
int symbol = lz.getByte(readAhead) | 0x100;
if (state.isLiteral()) {
int subencoderIndex;
int bit;
do {
subencoderIndex = symbol >>> 8;
bit = (symbol >>> 7) & 1;
rc.encodeBit(probs, subencoderIndex, bit);
symbol <<= 1;
} while (symbol < 0x10000);
} else {
int matchByte = lz.getByte(reps[0] + 1 + readAhead);
int offset = 0x100;
int subencoderIndex;
int matchBit;
int bit;
do {
matchByte <<= 1;
matchBit = matchByte & offset;
subencoderIndex = offset + matchBit + (symbol >>> 8);
bit = (symbol >>> 7) & 1;
rc.encodeBit(probs, subencoderIndex, bit);
symbol <<= 1;
offset &= ~(matchByte ^ symbol);
} while (symbol < 0x10000);
}
state.updateLiteral();
}
int getNormalPrice(int symbol) {
int price = 0;
int subencoderIndex;
int bit;
symbol |= 0x100;
do {
subencoderIndex = symbol >>> 8;
bit = (symbol >>> 7) & 1;
price += RangeEncoder.getBitPrice(probs[subencoderIndex],
bit);
symbol <<= 1;
} while (symbol < (0x100 << 8));
return price;
}
int getMatchedPrice(int symbol, int matchByte) {
int price = 0;
int offset = 0x100;
int subencoderIndex;
int matchBit;
int bit;
symbol |= 0x100;
do {
matchByte <<= 1;
matchBit = matchByte & offset;
subencoderIndex = offset + matchBit + (symbol >>> 8);
bit = (symbol >>> 7) & 1;
price += RangeEncoder.getBitPrice(probs[subencoderIndex],
bit);
symbol <<= 1;
offset &= ~(matchByte ^ symbol);
} while (symbol < (0x100 << 8));
return price;
}
}
}
class LengthEncoder extends LengthCoder {
/**
* The prices are updated after at least
* <code>PRICE_UPDATE_INTERVAL</code> many lengths have been encoded
* with the same posState.
*/
private static final int PRICE_UPDATE_INTERVAL = 32; // FIXME?
private final int[] counters;
private final int[][] prices;
LengthEncoder(int pb, int niceLen) {
int posStates = 1 << pb;
counters = new int[posStates];
// Always allocate at least LOW_SYMBOLS + MID_SYMBOLS because
// it makes updatePrices slightly simpler. The prices aren't
// usually needed anyway if niceLen < 18.
int lenSymbols = Math.max(niceLen - MATCH_LEN_MIN + 1,
LOW_SYMBOLS + MID_SYMBOLS);
prices = new int[posStates][lenSymbols];
}
void reset() {
super.reset();
// Reset counters to zero to force price update before
// the prices are needed.
for (int i = 0; i < counters.length; ++i)
counters[i] = 0;
}
void encode(int len, int posState) {
len -= MATCH_LEN_MIN;
if (len < LOW_SYMBOLS) {
rc.encodeBit(choice, 0, 0);
rc.encodeBitTree(low[posState], len);
} else {
rc.encodeBit(choice, 0, 1);
len -= LOW_SYMBOLS;
if (len < MID_SYMBOLS) {
rc.encodeBit(choice, 1, 0);
rc.encodeBitTree(mid[posState], len);
} else {
rc.encodeBit(choice, 1, 1);
rc.encodeBitTree(high, len - MID_SYMBOLS);
}
}
--counters[posState];
}
int getPrice(int len, int posState) {
return prices[posState][len - MATCH_LEN_MIN];
}
void updatePrices() {
for (int posState = 0; posState < counters.length; ++posState)
if (counters[posState] <= 0) {
counters[posState] = PRICE_UPDATE_INTERVAL;
updatePrices(posState);
}
}
private void updatePrices(int posState) {
int choice0Price = RangeEncoder.getBitPrice(choice[0], 0);
int i = 0;
for (; i < LOW_SYMBOLS; ++i)
prices[posState][i] = choice0Price
+ RangeEncoder.getBitTreePrice(low[posState], i);
choice0Price = RangeEncoder.getBitPrice(choice[0], 1);
int choice1Price = RangeEncoder.getBitPrice(choice[1], 0);
for (; i < LOW_SYMBOLS + MID_SYMBOLS; ++i)
prices[posState][i] = choice0Price + choice1Price
+ RangeEncoder.getBitTreePrice(mid[posState],
i - LOW_SYMBOLS);
choice1Price = RangeEncoder.getBitPrice(choice[1], 1);
for (; i < prices[posState].length; ++i)
prices[posState][i] = choice0Price + choice1Price
+ RangeEncoder.getBitTreePrice(high, i - LOW_SYMBOLS
- MID_SYMBOLS);
}
}
}

View File

@@ -1,150 +0,0 @@
/*
* LZMAEncoderFast
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.lzma;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.lz.Matches;
import org.tukaani.xz.rangecoder.RangeEncoder;
final class LZMAEncoderFast extends LZMAEncoder {
private static int EXTRA_SIZE_BEFORE = 1;
private static int EXTRA_SIZE_AFTER = MATCH_LEN_MAX - 1;
private Matches matches = null;
static int getMemoryUsage(int dictSize, int extraSizeBefore, int mf) {
return LZEncoder.getMemoryUsage(
dictSize, Math.max(extraSizeBefore, EXTRA_SIZE_BEFORE),
EXTRA_SIZE_AFTER, MATCH_LEN_MAX, mf);
}
LZMAEncoderFast(RangeEncoder rc, int lc, int lp, int pb,
int dictSize, int extraSizeBefore,
int niceLen, int mf, int depthLimit) {
super(rc, LZEncoder.getInstance(dictSize,
Math.max(extraSizeBefore,
EXTRA_SIZE_BEFORE),
EXTRA_SIZE_AFTER,
niceLen, MATCH_LEN_MAX,
mf, depthLimit),
lc, lp, pb, dictSize, niceLen);
}
private boolean changePair(int smallDist, int bigDist) {
return smallDist < (bigDist >>> 7);
}
int getNextSymbol() {
// Get the matches for the next byte unless readAhead indicates
// that we already got the new matches during the previous call
// to this function.
if (readAhead == -1)
matches = getMatches();
back = -1;
// Get the number of bytes available in the dictionary, but
// not more than the maximum match length. If there aren't
// enough bytes remaining to encode a match at all, return
// immediately to encode this byte as a literal.
int avail = Math.min(lz.getAvail(), MATCH_LEN_MAX);
if (avail < MATCH_LEN_MIN)
return 1;
// Look for a match from the previous four match distances.
int bestRepLen = 0;
int bestRepIndex = 0;
for (int rep = 0; rep < REPS; ++rep) {
int len = lz.getMatchLen(reps[rep], avail);
if (len < MATCH_LEN_MIN)
continue;
// If it is long enough, return it.
if (len >= niceLen) {
back = rep;
skip(len - 1);
return len;
}
// Remember the index and length of the best repeated match.
if (len > bestRepLen) {
bestRepIndex = rep;
bestRepLen = len;
}
}
int mainLen = 0;
int mainDist = 0;
if (matches.count > 0) {
mainLen = matches.len[matches.count - 1];
mainDist = matches.dist[matches.count - 1];
if (mainLen >= niceLen) {
back = mainDist + REPS;
skip(mainLen - 1);
return mainLen;
}
while (matches.count > 1
&& mainLen == matches.len[matches.count - 2] + 1) {
if (!changePair(matches.dist[matches.count - 2], mainDist))
break;
--matches.count;
mainLen = matches.len[matches.count - 1];
mainDist = matches.dist[matches.count - 1];
}
if (mainLen == MATCH_LEN_MIN && mainDist >= 0x80)
mainLen = 1;
}
if (bestRepLen >= MATCH_LEN_MIN)
if (bestRepLen + 1 >= mainLen
|| (bestRepLen + 2 >= mainLen && mainDist >= (1 << 9))
|| (bestRepLen + 3 >= mainLen && mainDist >= (1 << 15))) {
back = bestRepIndex;
skip(bestRepLen - 1);
return bestRepLen;
}
if (mainLen < MATCH_LEN_MIN || avail <= MATCH_LEN_MIN)
return 1;
// Get the next match. Test if it is better than the current match.
// If so, encode the current byte as a literal.
matches = getMatches();
if (matches.count > 0) {
int newLen = matches.len[matches.count - 1];
int newDist = matches.dist[matches.count - 1];
if ((newLen >= mainLen && newDist < mainDist)
|| (newLen == mainLen + 1
&& !changePair(mainDist, newDist))
|| newLen > mainLen + 1
|| (newLen + 1 >= mainLen
&& mainLen >= MATCH_LEN_MIN + 1
&& changePair(newDist, mainDist)))
return 1;
}
int limit = Math.max(mainLen - 1, MATCH_LEN_MIN);
for (int rep = 0; rep < REPS; ++rep)
if (lz.getMatchLen(reps[rep], limit) == limit)
return 1;
back = mainDist + REPS;
skip(mainLen - 2);
return mainLen;
}
}

View File

@@ -1,561 +0,0 @@
/*
* LZMAEncoderNormal
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.lzma;
import org.tukaani.xz.lz.LZEncoder;
import org.tukaani.xz.lz.Matches;
import org.tukaani.xz.rangecoder.RangeEncoder;
final class LZMAEncoderNormal extends LZMAEncoder {
private static final int OPTS = 4096;
private static int EXTRA_SIZE_BEFORE = OPTS;
private static int EXTRA_SIZE_AFTER = OPTS;
private final Optimum[] opts = new Optimum[OPTS];
private int optCur = 0;
private int optEnd = 0;
private Matches matches;
// These are fields solely to avoid allocating the objects again and
// again on each function call.
private final int[] repLens = new int[REPS];
private final State nextState = new State();
static int getMemoryUsage(int dictSize, int extraSizeBefore, int mf) {
return LZEncoder.getMemoryUsage(dictSize,
Math.max(extraSizeBefore, EXTRA_SIZE_BEFORE),
EXTRA_SIZE_AFTER, MATCH_LEN_MAX, mf)
+ OPTS * 64 / 1024;
}
LZMAEncoderNormal(RangeEncoder rc, int lc, int lp, int pb,
int dictSize, int extraSizeBefore,
int niceLen, int mf, int depthLimit) {
super(rc, LZEncoder.getInstance(dictSize,
Math.max(extraSizeBefore,
EXTRA_SIZE_BEFORE),
EXTRA_SIZE_AFTER,
niceLen, MATCH_LEN_MAX,
mf, depthLimit),
lc, lp, pb, dictSize, niceLen);
for (int i = 0; i < OPTS; ++i)
opts[i] = new Optimum();
}
public void reset() {
optCur = 0;
optEnd = 0;
super.reset();
}
/**
* Converts the opts array from backward indexes to forward indexes. Then it
* will be simple to get the next symbol from the array in later calls to
* <code>getNextSymbol()</code>.
*/
private int convertOpts() {
optEnd = optCur;
int optPrev = opts[optCur].optPrev;
do {
Optimum opt = opts[optCur];
if (opt.prev1IsLiteral) {
opts[optPrev].optPrev = optCur;
opts[optPrev].backPrev = -1;
optCur = optPrev--;
if (opt.hasPrev2) {
opts[optPrev].optPrev = optPrev + 1;
opts[optPrev].backPrev = opt.backPrev2;
optCur = optPrev;
optPrev = opt.optPrev2;
}
}
int temp = opts[optPrev].optPrev;
opts[optPrev].optPrev = optCur;
optCur = optPrev;
optPrev = temp;
} while (optCur > 0);
optCur = opts[0].optPrev;
back = opts[optCur].backPrev;
return optCur;
}
int getNextSymbol() {
// If there are pending symbols from an earlier call to this
// function, return those symbols first.
if (optCur < optEnd) {
int len = opts[optCur].optPrev - optCur;
optCur = opts[optCur].optPrev;
back = opts[optCur].backPrev;
return len;
}
assert optCur == optEnd;
optCur = 0;
optEnd = 0;
back = -1;
if (readAhead == -1)
matches = getMatches();
// Get the number of bytes available in the dictionary, but
// not more than the maximum match length. If there aren't
// enough bytes remaining to encode a match at all, return
// immediately to encode this byte as a literal.
int avail = Math.min(lz.getAvail(), MATCH_LEN_MAX);
if (avail < MATCH_LEN_MIN)
return 1;
// Get the lengths of repeated matches.
int repBest = 0;
for (int rep = 0; rep < REPS; ++rep) {
repLens[rep] = lz.getMatchLen(reps[rep], avail);
if (repLens[rep] < MATCH_LEN_MIN) {
repLens[rep] = 0;
continue;
}
if (repLens[rep] > repLens[repBest])
repBest = rep;
}
// Return if the best repeated match is at least niceLen bytes long.
if (repLens[repBest] >= niceLen) {
back = repBest;
skip(repLens[repBest] - 1);
return repLens[repBest];
}
// Initialize mainLen and mainDist to the longest match found
// by the match finder.
int mainLen = 0;
int mainDist = 0;
if (matches.count > 0) {
mainLen = matches.len[matches.count - 1];
mainDist = matches.dist[matches.count - 1];
// Return if it is at least niceLen bytes long.
if (mainLen >= niceLen) {
back = mainDist + REPS;
skip(mainLen - 1);
return mainLen;
}
}
int curByte = lz.getByte(0);
int matchByte = lz.getByte(reps[0] + 1);
// If the match finder found no matches and this byte cannot be
// encoded as a repeated match (short or long), we must be return
// to have the byte encoded as a literal.
if (mainLen < MATCH_LEN_MIN && curByte != matchByte
&& repLens[repBest] < MATCH_LEN_MIN)
return 1;
int pos = lz.getPos();
int posState = pos & posMask;
// Calculate the price of encoding the current byte as a literal.
{
int prevByte = lz.getByte(1);
int literalPrice = literalEncoder.getPrice(curByte, matchByte,
prevByte, pos, state);
opts[1].set1(literalPrice, 0, -1);
}
int anyMatchPrice = getAnyMatchPrice(state, posState);
int anyRepPrice = getAnyRepPrice(anyMatchPrice, state);
// If it is possible to encode this byte as a short rep, see if
// it is cheaper than encoding it as a literal.
if (matchByte == curByte) {
int shortRepPrice = getShortRepPrice(anyRepPrice,
state, posState);
if (shortRepPrice < opts[1].price)
opts[1].set1(shortRepPrice, 0, 0);
}
// Return if there is neither normal nor long repeated match. Use
// a short match instead of a literal if is is possible and cheaper.
optEnd = Math.max(mainLen, repLens[repBest]);
if (optEnd < MATCH_LEN_MIN) {
assert optEnd == 0 : optEnd;
back = opts[1].backPrev;
return 1;
}
// Update the lookup tables for distances and lengths before using
// those price calculation functions. (The price function above
// don't need these tables.)
updatePrices();
// Initialize the state and reps of this position in opts[].
// updateOptStateAndReps() will need these to get the new
// state and reps for the next byte.
opts[0].state.set(state);
System.arraycopy(reps, 0, opts[0].reps, 0, REPS);
// Initialize the prices for latter opts that will be used below.
for (int i = optEnd; i >= MATCH_LEN_MIN; --i)
opts[i].reset();
// Calculate the prices of repeated matches of all lengths.
for (int rep = 0; rep < REPS; ++rep) {
int repLen = repLens[rep];
if (repLen < MATCH_LEN_MIN)
continue;
int longRepPrice = getLongRepPrice(anyRepPrice, rep,
state, posState);
do {
int price = longRepPrice + repLenEncoder.getPrice(repLen,
posState);
if (price < opts[repLen].price)
opts[repLen].set1(price, 0, rep);
} while (--repLen >= MATCH_LEN_MIN);
}
// Calculate the prices of normal matches that are longer than rep0.
{
int len = Math.max(repLens[0] + 1, MATCH_LEN_MIN);
if (len <= mainLen) {
int normalMatchPrice = getNormalMatchPrice(anyMatchPrice,
state);
// Set i to the index of the shortest match that is
// at least len bytes long.
int i = 0;
while (len > matches.len[i])
++i;
while (true) {
int dist = matches.dist[i];
int price = getMatchAndLenPrice(normalMatchPrice,
dist, len, posState);
if (price < opts[len].price)
opts[len].set1(price, 0, dist + REPS);
if (len == matches.len[i])
if (++i == matches.count)
break;
++len;
}
}
}
avail = Math.min(lz.getAvail(), OPTS - 1);
// Get matches for later bytes and optimize the use of LZMA symbols
// by calculating the prices and picking the cheapest symbol
// combinations.
while (++optCur < optEnd) {
matches = getMatches();
if (matches.count > 0
&& matches.len[matches.count - 1] >= niceLen)
break;
--avail;
++pos;
posState = pos & posMask;
updateOptStateAndReps();
anyMatchPrice = opts[optCur].price
+ getAnyMatchPrice(opts[optCur].state, posState);
anyRepPrice = getAnyRepPrice(anyMatchPrice, opts[optCur].state);
calc1BytePrices(pos, posState, avail, anyRepPrice);
if (avail >= MATCH_LEN_MIN) {
int startLen = calcLongRepPrices(pos, posState,
avail, anyRepPrice);
if (matches.count > 0)
calcNormalMatchPrices(pos, posState, avail,
anyMatchPrice, startLen);
}
}
return convertOpts();
}
/**
* Updates the state and reps for the current byte in the opts array.
*/
private void updateOptStateAndReps() {
int optPrev = opts[optCur].optPrev;
assert optPrev < optCur;
if (opts[optCur].prev1IsLiteral) {
--optPrev;
if (opts[optCur].hasPrev2) {
opts[optCur].state.set(opts[opts[optCur].optPrev2].state);
if (opts[optCur].backPrev2 < REPS)
opts[optCur].state.updateLongRep();
else
opts[optCur].state.updateMatch();
} else
opts[optCur].state.set(opts[optPrev].state);
opts[optCur].state.updateLiteral();
} else
opts[optCur].state.set(opts[optPrev].state);
if (optPrev == optCur - 1) {
// Must be either a short rep or a literal.
assert opts[optCur].backPrev == 0 || opts[optCur].backPrev == -1;
if (opts[optCur].backPrev == 0)
opts[optCur].state.updateShortRep();
else
opts[optCur].state.updateLiteral();
System.arraycopy(opts[optPrev].reps, 0,
opts[optCur].reps, 0, REPS);
} else {
int back;
if (opts[optCur].prev1IsLiteral && opts[optCur].hasPrev2) {
optPrev = opts[optCur].optPrev2;
back = opts[optCur].backPrev2;
opts[optCur].state.updateLongRep();
} else {
back = opts[optCur].backPrev;
if (back < REPS)
opts[optCur].state.updateLongRep();
else
opts[optCur].state.updateMatch();
}
if (back < REPS) {
opts[optCur].reps[0] = opts[optPrev].reps[back];
int rep;
for (rep = 1; rep <= back; ++rep)
opts[optCur].reps[rep] = opts[optPrev].reps[rep - 1];
for (; rep < REPS; ++rep)
opts[optCur].reps[rep] = opts[optPrev].reps[rep];
} else {
opts[optCur].reps[0] = back - REPS;
System.arraycopy(opts[optPrev].reps, 0,
opts[optCur].reps, 1, REPS - 1);
}
}
}
/**
* Calculates prices of a literal, a short rep, and literal + rep0.
*/
private void calc1BytePrices(int pos, int posState,
int avail, int anyRepPrice) {
// This will be set to true if using a literal or a short rep.
boolean nextIsByte = false;
int curByte = lz.getByte(0);
int matchByte = lz.getByte(opts[optCur].reps[0] + 1);
// Try a literal.
int literalPrice = opts[optCur].price
+ literalEncoder.getPrice(curByte, matchByte, lz.getByte(1),
pos, opts[optCur].state);
if (literalPrice < opts[optCur + 1].price) {
opts[optCur + 1].set1(literalPrice, optCur, -1);
nextIsByte = true;
}
// Try a short rep.
if (matchByte == curByte && (opts[optCur + 1].optPrev == optCur
|| opts[optCur + 1].backPrev != 0)) {
int shortRepPrice = getShortRepPrice(anyRepPrice,
opts[optCur].state,
posState);
if (shortRepPrice <= opts[optCur + 1].price) {
opts[optCur + 1].set1(shortRepPrice, optCur, 0);
nextIsByte = true;
}
}
// If neither a literal nor a short rep was the cheapest choice,
// try literal + long rep0.
if (!nextIsByte && matchByte != curByte && avail > MATCH_LEN_MIN) {
int lenLimit = Math.min(niceLen, avail - 1);
int len = lz.getMatchLen(1, opts[optCur].reps[0], lenLimit);
if (len >= MATCH_LEN_MIN) {
nextState.set(opts[optCur].state);
nextState.updateLiteral();
int nextPosState = (pos + 1) & posMask;
int price = literalPrice
+ getLongRepAndLenPrice(0, len,
nextState, nextPosState);
int i = optCur + 1 + len;
while (optEnd < i)
opts[++optEnd].reset();
if (price < opts[i].price)
opts[i].set2(price, optCur, 0);
}
}
}
/**
* Calculates prices of long rep and long rep + literal + rep0.
*/
private int calcLongRepPrices(int pos, int posState,
int avail, int anyRepPrice) {
int startLen = MATCH_LEN_MIN;
int lenLimit = Math.min(avail, niceLen);
for (int rep = 0; rep < REPS; ++rep) {
int len = lz.getMatchLen(opts[optCur].reps[rep], lenLimit);
if (len < MATCH_LEN_MIN)
continue;
while (optEnd < optCur + len)
opts[++optEnd].reset();
int longRepPrice = getLongRepPrice(anyRepPrice, rep,
opts[optCur].state, posState);
for (int i = len; i >= MATCH_LEN_MIN; --i) {
int price = longRepPrice
+ repLenEncoder.getPrice(i, posState);
if (price < opts[optCur + i].price)
opts[optCur + i].set1(price, optCur, rep);
}
if (rep == 0)
startLen = len + 1;
int len2Limit = Math.min(niceLen, avail - len - 1);
int len2 = lz.getMatchLen(len + 1, opts[optCur].reps[rep],
len2Limit);
if (len2 >= MATCH_LEN_MIN) {
// Rep
int price = longRepPrice
+ repLenEncoder.getPrice(len, posState);
nextState.set(opts[optCur].state);
nextState.updateLongRep();
// Literal
int curByte = lz.getByte(len, 0);
int matchByte = lz.getByte(0); // lz.getByte(len, len)
int prevByte = lz.getByte(len, 1);
price += literalEncoder.getPrice(curByte, matchByte, prevByte,
pos + len, nextState);
nextState.updateLiteral();
// Rep0
int nextPosState = (pos + len + 1) & posMask;
price += getLongRepAndLenPrice(0, len2,
nextState, nextPosState);
int i = optCur + len + 1 + len2;
while (optEnd < i)
opts[++optEnd].reset();
if (price < opts[i].price)
opts[i].set3(price, optCur, rep, len, 0);
}
}
return startLen;
}
/**
* Calculates prices of a normal match and normal match + literal + rep0.
*/
private void calcNormalMatchPrices(int pos, int posState, int avail,
int anyMatchPrice, int startLen) {
// If the longest match is so long that it would not fit into
// the opts array, shorten the matches.
if (matches.len[matches.count - 1] > avail) {
matches.count = 0;
while (matches.len[matches.count] < avail)
++matches.count;
matches.len[matches.count++] = avail;
}
if (matches.len[matches.count - 1] < startLen)
return;
while (optEnd < optCur + matches.len[matches.count - 1])
opts[++optEnd].reset();
int normalMatchPrice = getNormalMatchPrice(anyMatchPrice,
opts[optCur].state);
int match = 0;
while (startLen > matches.len[match])
++match;
for (int len = startLen;; ++len) {
int dist = matches.dist[match];
// Calculate the price of a match of len bytes from the nearest
// possible distance.
int matchAndLenPrice = getMatchAndLenPrice(normalMatchPrice,
dist, len, posState);
if (matchAndLenPrice < opts[optCur + len].price)
opts[optCur + len].set1(matchAndLenPrice,
optCur, dist + REPS);
if (len != matches.len[match])
continue;
// Try match + literal + rep0. First get the length of the rep0.
int len2Limit = Math.min(niceLen, avail - len - 1);
int len2 = lz.getMatchLen(len + 1, dist, len2Limit);
if (len2 >= MATCH_LEN_MIN) {
nextState.set(opts[optCur].state);
nextState.updateMatch();
// Literal
int curByte = lz.getByte(len, 0);
int matchByte = lz.getByte(0); // lz.getByte(len, len)
int prevByte = lz.getByte(len, 1);
int price = matchAndLenPrice
+ literalEncoder.getPrice(curByte, matchByte,
prevByte, pos + len,
nextState);
nextState.updateLiteral();
// Rep0
int nextPosState = (pos + len + 1) & posMask;
price += getLongRepAndLenPrice(0, len2,
nextState, nextPosState);
int i = optCur + len + 1 + len2;
while (optEnd < i)
opts[++optEnd].reset();
if (price < opts[i].price)
opts[i].set3(price, optCur, dist + REPS, len, 0);
}
if (++match == matches.count)
break;
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Optimum
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.lzma;
final class Optimum {
private static final int INFINITY_PRICE = 1 << 30;
final State state = new State();
final int[] reps = new int[LZMACoder.REPS];
/**
* Cumulative price of arriving to this byte.
*/
int price;
int optPrev;
int backPrev;
boolean prev1IsLiteral;
boolean hasPrev2;
int optPrev2;
int backPrev2;
/**
* Resets the price.
*/
void reset() {
price = INFINITY_PRICE;
}
/**
* Sets to indicate one LZMA symbol (literal, rep, or match).
*/
void set1(int newPrice, int optCur, int back) {
price = newPrice;
optPrev = optCur;
backPrev = back;
prev1IsLiteral = false;
}
/**
* Sets to indicate two LZMA symbols of which the first one is a literal.
*/
void set2(int newPrice, int optCur, int back) {
price = newPrice;
optPrev = optCur + 1;
backPrev = back;
prev1IsLiteral = true;
hasPrev2 = false;
}
/**
* Sets to indicate three LZMA symbols of which the second one is a literal.
*/
void set3(int newPrice, int optCur, int back2, int len2, int back) {
price = newPrice;
optPrev = optCur + len2 + 1;
backPrev = back;
prev1IsLiteral = true;
hasPrev2 = true;
optPrev2 = optCur;
backPrev2 = back2;
}
}

View File

@@ -1,76 +0,0 @@
/*
* State
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.lzma;
final class State {
static final int STATES = 12;
private static final int LIT_STATES = 7;
private static final int LIT_LIT = 0;
private static final int MATCH_LIT_LIT = 1;
private static final int REP_LIT_LIT = 2;
private static final int SHORTREP_LIT_LIT = 3;
private static final int MATCH_LIT = 4;
private static final int REP_LIT = 5;
private static final int SHORTREP_LIT = 6;
private static final int LIT_MATCH = 7;
private static final int LIT_LONGREP = 8;
private static final int LIT_SHORTREP = 9;
private static final int NONLIT_MATCH = 10;
private static final int NONLIT_REP = 11;
private int state;
State() {
}
State(State other) {
state = other.state;
}
void reset() {
state = LIT_LIT;
}
int get() {
return state;
}
void set(State other) {
state = other.state;
}
void updateLiteral() {
if (state <= SHORTREP_LIT_LIT)
state = LIT_LIT;
else if (state <= LIT_SHORTREP)
state -= 3;
else
state -= 6;
}
void updateMatch() {
state = state < LIT_STATES ? LIT_MATCH : NONLIT_MATCH;
}
void updateLongRep() {
state = state < LIT_STATES ? LIT_LONGREP : NONLIT_REP;
}
void updateShortRep() {
state = state < LIT_STATES ? LIT_SHORTREP : NONLIT_REP;
}
boolean isLiteral() {
return state < LIT_STATES;
}
}

View File

@@ -1,49 +0,0 @@
/*
* BCJ filter for little endian ARM instructions
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.simple;
public final class ARM implements SimpleFilter {
private final boolean isEncoder;
private int pos;
public ARM(boolean isEncoder, int startPos) {
this.isEncoder = isEncoder;
pos = startPos + 8;
}
public int code(byte[] buf, int off, int len) {
int end = off + len - 4;
int i;
for (i = off; i <= end; i += 4)
if ((buf[i + 3] & 0xFF) == 0xEB) {
int src = ((buf[i + 2] & 0xFF) << 16)
| ((buf[i + 1] & 0xFF) << 8)
| (buf[i] & 0xFF);
src <<= 2;
int dest;
if (isEncoder)
dest = src + (pos + i - off);
else
dest = src - (pos + i - off);
dest >>>= 2;
buf[i + 2] = (byte) (dest >>> 16);
buf[i + 1] = (byte) (dest >>> 8);
buf[i] = (byte) dest;
}
i -= off;
pos += i;
return i;
}
}

View File

@@ -1,52 +0,0 @@
/*
* BCJ filter for little endian ARM-Thumb instructions
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <http://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz.simple;
public final class ARMThumb implements SimpleFilter {
private final boolean isEncoder;
private int pos;
public ARMThumb(boolean isEncoder, int startPos) {
this.isEncoder = isEncoder;
pos = startPos + 4;
}
public int code(byte[] buf, int off, int len) {
int end = off + len - 4;
int i;
for (i = off; i <= end; i += 2)
if ((buf[i + 1] & 0xF8) == 0xF0 && (buf[i + 3] & 0xF8) == 0xF8) {
int src = ((buf[i + 1] & 0x07) << 19)
| ((buf[i] & 0xFF) << 11)
| ((buf[i + 3] & 0x07) << 8)
| (buf[i + 2] & 0xFF);
src <<= 1;
int dest;
if (isEncoder)
dest = src + (pos + i - off);
else
dest = src - (pos + i - off);
dest >>>= 1;
buf[i + 1] = (byte) (0xF0 | ((dest >>> 19) & 0x07));
buf[i] = (byte) (dest >>> 11);
buf[i + 3] = (byte) (0xF8 | ((dest >>> 8) & 0x07));
buf[i + 2] = (byte) dest;
i += 2;
}
i -= off;
pos += i;
return i;
}
}