upgrade to latest lzf trunk

This commit is contained in:
kimchy 2011-07-16 21:55:13 +03:00
parent 1a3306912e
commit c28421b1ed
4 changed files with 242 additions and 49 deletions

View File

@ -109,7 +109,10 @@ public class LZFDecoder {
} }
/** /**
* Main decode from a stream. Decompressed bytes are placed in the outputBuffer, inputBuffer is a "scratch-area". * Main decode from a stream. Decompressed bytes are placed in the outputBuffer, inputBuffer
* is a "scratch-area".
* <p>
* If no
* *
* @param is An input stream of LZF compressed bytes * @param is An input stream of LZF compressed bytes
* @param inputBuffer A byte array used as a scratch area. * @param inputBuffer A byte array used as a scratch area.
@ -119,18 +122,19 @@ public class LZFDecoder {
public static int decompressChunk(final InputStream is, final byte[] inputBuffer, final byte[] outputBuffer) public static int decompressChunk(final InputStream is, final byte[] inputBuffer, final byte[] outputBuffer)
throws IOException { throws IOException {
int bytesInOutput; int bytesInOutput;
int headerLength = is.read(inputBuffer, 0, HEADER_BYTES); /* note: we do NOT read more than 5 bytes because otherwise might need to shuffle bytes
if (headerLength != HEADER_BYTES) { * for output buffer (could perhaps optimize in future?)
return -1; */
int bytesRead = readHeader(is, inputBuffer);
if ((bytesRead < HEADER_BYTES)
|| inputBuffer[0] != LZFChunk.BYTE_Z || inputBuffer[1] != LZFChunk.BYTE_V) {
if (bytesRead == 0) { // probably fine, clean EOF
return -1;
}
throw new IOException("Corrupt input data, block did not start with 2 byte signature ('ZV') followed by type byte, 2-byte length)");
} }
int inPtr = 0; int type = inputBuffer[2];
if (inputBuffer[inPtr] != LZFChunk.BYTE_Z || inputBuffer[inPtr + 1] != LZFChunk.BYTE_V) { int compLen = uint16(inputBuffer, 3);
throw new IOException("Corrupt input data, block did not start with 'ZV' signature bytes");
}
inPtr += 2;
int type = inputBuffer[inPtr++];
int compLen = uint16(inputBuffer, inPtr);
inPtr += 2;
if (type == LZFChunk.BLOCK_TYPE_NON_COMPRESSED) { // uncompressed if (type == LZFChunk.BLOCK_TYPE_NON_COMPRESSED) { // uncompressed
readFully(is, false, outputBuffer, 0, compLen); readFully(is, false, outputBuffer, 0, compLen);
bytesInOutput = compLen; bytesInOutput = compLen;
@ -200,6 +204,38 @@ public class LZFDecoder {
return ((data[ptr] & 0xFF) << 8) + (data[ptr + 1] & 0xFF); return ((data[ptr] & 0xFF) << 8) + (data[ptr + 1] & 0xFF);
} }
/**
* Helper method to forcibly load header bytes that must be read before
* chunk can be handled.
*/
protected static int readHeader(final InputStream is, final byte[] inputBuffer)
throws IOException {
// Ok: simple case first, where we just get all data we need
int needed = HEADER_BYTES;
int count = is.read(inputBuffer, 0, needed);
if (count == needed) {
return count;
}
if (count <= 0) {
return 0;
}
// if not, a source that trickles data (network etc); must loop
int offset = count;
needed -= count;
do {
count = is.read(inputBuffer, offset, needed);
if (count <= 0) {
break;
}
offset += count;
needed -= count;
} while (needed > 0);
return offset;
}
private final static void readFully(InputStream is, boolean compressed, private final static void readFully(InputStream is, boolean compressed,
byte[] outputBuffer, int offset, int len) throws IOException { byte[] outputBuffer, int offset, int len) throws IOException {
int left = len; int left = len;

View File

@ -11,6 +11,12 @@ public class LZFInputStream extends InputStream {
*/ */
protected final InputStream inputStream; protected final InputStream inputStream;
/**
* Flag that indicates if we have already called 'inputStream.close()'
* (to avoid calling it multiple times)
*/
protected boolean inputStreamClosed;
/** /**
* Flag that indicates whether we force full reads (reading of as many * Flag that indicates whether we force full reads (reading of as many
* bytes as requested), or 'optimal' reads (up to as many as available, * bytes as requested), or 'optimal' reads (up to as many as available,
@ -31,6 +37,12 @@ public class LZFInputStream extends InputStream {
/* Length of the current uncompressed bytes buffer */ /* Length of the current uncompressed bytes buffer */
private int bufferLength = 0; private int bufferLength = 0;
/*
///////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////
*/
public LZFInputStream(final InputStream inputStream) throws IOException { public LZFInputStream(final InputStream inputStream) throws IOException {
this(inputStream, false); this(inputStream, false);
} }
@ -45,31 +57,56 @@ public class LZFInputStream extends InputStream {
super(); super();
_recycler = BufferRecycler.instance(); _recycler = BufferRecycler.instance();
inputStream = in; inputStream = in;
inputStreamClosed = false;
cfgFullReads = fullReads; cfgFullReads = fullReads;
_inputBuffer = _recycler.allocInputBuffer(LZFChunk.MAX_CHUNK_LEN); _inputBuffer = _recycler.allocInputBuffer(LZFChunk.MAX_CHUNK_LEN);
_decodedBytes = _recycler.allocDecodeBuffer(LZFChunk.MAX_CHUNK_LEN); _decodedBytes = _recycler.allocDecodeBuffer(LZFChunk.MAX_CHUNK_LEN);
} }
/*
///////////////////////////////////////////////////////////////////////
// InputStream impl
///////////////////////////////////////////////////////////////////////
*/
/**
* Method is overridden to report number of bytes that can now be read
* from decoded data buffer, without reading bytes from the underlying
* stream.
* Never throws an exception; returns number of bytes available without
* further reads from underlying source; -1 if stream has been closed, or
* 0 if an actual read (and possible blocking) is needed to find out.
*/
@Override @Override
public int read() throws IOException { public int available() {
readyBuffer(); // if closed, return -1;
if (bufferPosition < bufferLength) { if (inputStreamClosed) {
return _decodedBytes[bufferPosition++] & 255; return -1;
} }
return -1; int left = (bufferLength - bufferPosition);
return (left <= 0) ? 0 : left;
} }
@Override
public int read() throws IOException {
if (!readyBuffer()) {
return -1;
}
return _decodedBytes[bufferPosition++] & 255;
}
@Override
public int read(final byte[] buffer) throws IOException { public int read(final byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length); return read(buffer, 0, buffer.length);
} }
@Override
public int read(final byte[] buffer, int offset, int length) throws IOException { public int read(final byte[] buffer, int offset, int length) throws IOException {
if (length < 1) { if (length < 1) {
return 0; return 0;
} }
readyBuffer(); if (!readyBuffer()) {
if (bufferLength < 0) {
return -1; return -1;
} }
// First let's read however much data we happen to have... // First let's read however much data we happen to have...
@ -84,8 +121,7 @@ public class LZFInputStream extends InputStream {
int totalRead = chunkLength; int totalRead = chunkLength;
do { do {
offset += chunkLength; offset += chunkLength;
readyBuffer(); if (!readyBuffer()) {
if (bufferLength == -1) {
break; break;
} }
chunkLength = Math.min(bufferLength - bufferPosition, (length - totalRead)); chunkLength = Math.min(bufferLength - bufferPosition, (length - totalRead));
@ -97,6 +133,7 @@ public class LZFInputStream extends InputStream {
return totalRead; return totalRead;
} }
@Override
public void close() throws IOException { public void close() throws IOException {
bufferPosition = bufferLength = 0; bufferPosition = bufferLength = 0;
byte[] buf = _inputBuffer; byte[] buf = _inputBuffer;
@ -109,19 +146,53 @@ public class LZFInputStream extends InputStream {
_decodedBytes = null; _decodedBytes = null;
_recycler.releaseDecodeBuffer(buf); _recycler.releaseDecodeBuffer(buf);
} }
inputStream.close(); if (!inputStreamClosed) {
inputStreamClosed = true;
inputStream.close();
}
} }
/*
///////////////////////////////////////////////////////////////////////
// Additional public accessors
///////////////////////////////////////////////////////////////////////
*/
/**
* Method that can be used to find underlying {@link InputStream} that
* we read from to get LZF encoded data to decode.
* Will never return null; although underlying stream may be closed
* (if this stream has been closed).
*
* @since 0.8
*/
public InputStream getUnderlyingInputStream() {
return inputStream;
}
/*
///////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////
*/
/** /**
* Fill the uncompressed bytes buffer by reading the underlying inputStream. * Fill the uncompressed bytes buffer by reading the underlying inputStream.
* *
* @throws IOException * @throws IOException
*/ */
private final void readyBuffer() throws IOException { protected boolean readyBuffer() throws IOException {
if (bufferPosition >= bufferLength) { if (bufferPosition < bufferLength) {
bufferLength = LZFDecoder.decompressChunk(inputStream, _inputBuffer, _decodedBytes); return true;
bufferPosition = 0;
} }
if (inputStreamClosed) {
return false;
}
bufferLength = LZFDecoder.decompressChunk(inputStream, _inputBuffer, _decodedBytes);
if (bufferLength < 0) {
return false;
}
bufferPosition = 0;
return (bufferPosition < bufferLength);
} }
} }

View File

@ -17,13 +17,32 @@ public class LZFOutputStream extends OutputStream {
protected byte[] _outputBuffer; protected byte[] _outputBuffer;
protected int _position = 0; protected int _position = 0;
/**
* Flag that indicates if we have already called '_outputStream.close()'
* (to avoid calling it multiple times)
*/
protected boolean _outputStreamClosed;
/*
///////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////
*/
public LZFOutputStream(final OutputStream outputStream) { public LZFOutputStream(final OutputStream outputStream) {
_recycler = BufferRecycler.instance(); _recycler = BufferRecycler.instance();
_encoder = new ChunkEncoder(OUTPUT_BUFFER_SIZE, _recycler); _encoder = new ChunkEncoder(OUTPUT_BUFFER_SIZE, _recycler);
_outputStream = outputStream; _outputStream = outputStream;
_outputBuffer = _recycler.allocOutputBuffer(OUTPUT_BUFFER_SIZE); _outputBuffer = _recycler.allocOutputBuffer(OUTPUT_BUFFER_SIZE);
_outputStreamClosed = false;
} }
/*
///////////////////////////////////////////////////////////////////////
// OutputStream impl
///////////////////////////////////////////////////////////////////////
*/
@Override @Override
public void write(final int singleByte) throws IOException { public void write(final int singleByte) throws IOException {
if (_position >= _outputBuffer.length) { if (_position >= _outputBuffer.length) {
@ -75,19 +94,46 @@ public class LZFOutputStream extends OutputStream {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
flush(); flush();
_outputStream.close();
_encoder.close(); _encoder.close();
byte[] buf = _outputBuffer; byte[] buf = _outputBuffer;
if (buf != null) { if (buf != null) {
_outputBuffer = null; _outputBuffer = null;
_recycler.releaseOutputBuffer(buf); _recycler.releaseOutputBuffer(buf);
} }
if (!_outputStreamClosed) {
_outputStreamClosed = true;
_outputStream.close();
}
} }
/*
///////////////////////////////////////////////////////////////////////
// Additional public accessors
///////////////////////////////////////////////////////////////////////
*/
/**
* Method that can be used to find underlying {@link OutputStream} that
* we write encoded LZF encoded data into, after compressing it.
* Will never return null; although underlying stream may be closed
* (if this stream has been closed).
*
* @since 0.8
*/
public OutputStream getUnderlyingOutputStream() {
return _outputStream;
}
/*
///////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////
*/
/** /**
* Compress and write the current block to the OutputStream * Compress and write the current block to the OutputStream
*/ */
private void writeCompressedBlock() throws IOException { protected void writeCompressedBlock() throws IOException {
int left = _position; int left = _position;
_position = 0; _position = 0;
int offset = 0; int offset = 0;

View File

@ -37,6 +37,12 @@ public class LZFStreamInput extends StreamInput {
*/ */
protected StreamInput inputStream; protected StreamInput inputStream;
/**
* Flag that indicates if we have already called 'inputStream.close()'
* (to avoid calling it multiple times)
*/
protected boolean inputStreamClosed;
/** /**
* Flag that indicates whether we force full reads (reading of as many * Flag that indicates whether we force full reads (reading of as many
* bytes as requested), or 'optimal' reads (up to as many as available, * bytes as requested), or 'optimal' reads (up to as many as available,
@ -69,25 +75,44 @@ public class LZFStreamInput extends StreamInput {
_recycler = BufferRecycler.instance(); _recycler = BufferRecycler.instance();
} }
inputStream = in; inputStream = in;
inputStreamClosed = false;
_inputBuffer = _recycler.allocInputBuffer(LZFChunk.MAX_CHUNK_LEN); _inputBuffer = _recycler.allocInputBuffer(LZFChunk.MAX_CHUNK_LEN);
_decodedBytes = _recycler.allocDecodeBuffer(LZFChunk.MAX_CHUNK_LEN); _decodedBytes = _recycler.allocDecodeBuffer(LZFChunk.MAX_CHUNK_LEN);
} }
@Override public int read() throws IOException { /**
readyBuffer(); * Method is overridden to report number of bytes that can now be read
if (bufferPosition < bufferLength) { * from decoded data buffer, without reading bytes from the underlying
return _decodedBytes[bufferPosition++] & 255; * stream.
* Never throws an exception; returns number of bytes available without
* further reads from underlying source; -1 if stream has been closed, or
* 0 if an actual read (and possible blocking) is needed to find out.
*/
@Override
public int available() {
// if closed, return -1;
if (inputStreamClosed) {
return -1;
} }
return -1; int left = (bufferLength - bufferPosition);
return (left <= 0) ? 0 : left;
} }
@Override public int read(byte[] buffer, int offset, int length) throws IOException { @Override
public int read() throws IOException {
if (!readyBuffer()) {
return -1;
}
return _decodedBytes[bufferPosition++] & 255;
}
@Override
public int read(final byte[] buffer, int offset, int length) throws IOException {
if (length < 1) { if (length < 1) {
return 0; return 0;
} }
readyBuffer(); if (!readyBuffer()) {
if (bufferLength < 0) {
return -1; return -1;
} }
// First let's read however much data we happen to have... // First let's read however much data we happen to have...
@ -102,8 +127,7 @@ public class LZFStreamInput extends StreamInput {
int totalRead = chunkLength; int totalRead = chunkLength;
do { do {
offset += chunkLength; offset += chunkLength;
readyBuffer(); if (!readyBuffer()) {
if (bufferLength == -1) {
break; break;
} }
chunkLength = Math.min(bufferLength - bufferPosition, (length - totalRead)); chunkLength = Math.min(bufferLength - bufferPosition, (length - totalRead));
@ -116,11 +140,10 @@ public class LZFStreamInput extends StreamInput {
} }
@Override public byte readByte() throws IOException { @Override public byte readByte() throws IOException {
readyBuffer(); if (!readyBuffer()) {
if (bufferPosition < bufferLength) { throw new EOFException();
return _decodedBytes[bufferPosition++];
} }
throw new EOFException(); return _decodedBytes[bufferPosition++];
} }
@Override public void readBytes(byte[] b, int offset, int len) throws IOException { @Override public void readBytes(byte[] b, int offset, int len) throws IOException {
@ -165,18 +188,35 @@ public class LZFStreamInput extends StreamInput {
_decodedBytes = null; _decodedBytes = null;
_recycler.releaseDecodeBuffer(buf); _recycler.releaseDecodeBuffer(buf);
} }
inputStream.close(); if (!inputStreamClosed) {
inputStreamClosed = true;
inputStream.close();
}
} }
/*
///////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////
*/
/** /**
* Fill the uncompressed bytes buffer by reading the underlying inputStream. * Fill the uncompressed bytes buffer by reading the underlying inputStream.
* *
* @throws java.io.IOException * @throws IOException
*/ */
private void readyBuffer() throws IOException { protected boolean readyBuffer() throws IOException {
if (bufferPosition >= bufferLength) { if (bufferPosition < bufferLength) {
bufferLength = LZFDecoder.decompressChunk(inputStream, _inputBuffer, _decodedBytes); return true;
bufferPosition = 0;
} }
if (inputStreamClosed) {
return false;
}
bufferLength = LZFDecoder.decompressChunk(inputStream, _inputBuffer, _decodedBytes);
if (bufferLength < 0) {
return false;
}
bufferPosition = 0;
return (bufferPosition < bufferLength);
} }
} }