mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 11:05:29 -04:00
TMC-IOENC: Refactored Decoder to use ByteBuffer instead of byte[] for better readability/simpler code.
This commit is contained in:
parent
9a27f62dec
commit
f2ff00580a
@ -31,6 +31,7 @@ package com.twelvemonkeys.io.enc;
|
|||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for RLE decoding as specified by in the Windows BMP (aka DIB) file format.
|
* Abstract base class for RLE decoding as specified by in the Windows BMP (aka DIB) file format.
|
||||||
@ -86,25 +87,24 @@ abstract class AbstractRLEDecoder implements Decoder {
|
|||||||
* Decodes as much data as possible, from the stream into the buffer.
|
* Decodes as much data as possible, from the stream into the buffer.
|
||||||
*
|
*
|
||||||
* @param pStream the input stream containing RLE data
|
* @param pStream the input stream containing RLE data
|
||||||
* @param pBuffer tge buffer to decode the data to
|
* @param pBuffer the buffer to decode the data to
|
||||||
*
|
*
|
||||||
* @return the number of bytes decoded from the stream, to the buffer
|
* @return the number of bytes decoded from the stream, to the buffer
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O related exception ocurs while reading
|
* @throws IOException if an I/O related exception ocurs while reading
|
||||||
*/
|
*/
|
||||||
public final int decode(InputStream pStream, byte[] pBuffer) throws IOException {
|
public final int decode(InputStream pStream, ByteBuffer pBuffer) throws IOException {
|
||||||
int decoded = 0;
|
while (pBuffer.hasRemaining() && dstY >= 0) {
|
||||||
|
|
||||||
while (decoded < pBuffer.length && dstY >= 0) {
|
|
||||||
// NOTE: Decode only full rows, don't decode if y delta
|
// NOTE: Decode only full rows, don't decode if y delta
|
||||||
if (dstX == 0 && srcY == dstY) {
|
if (dstX == 0 && srcY == dstY) {
|
||||||
decodeRow(pStream);
|
decodeRow(pStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = Math.min(row.length - dstX, pBuffer.length - decoded);
|
int length = Math.min(row.length - dstX, pBuffer.remaining());
|
||||||
System.arraycopy(row, dstX, pBuffer, decoded, length);
|
// System.arraycopy(row, dstX, pBuffer, decoded, length);
|
||||||
|
pBuffer.put(row, 0, length);
|
||||||
dstX += length;
|
dstX += length;
|
||||||
decoded += length;
|
// decoded += length;
|
||||||
|
|
||||||
if (dstX == row.length) {
|
if (dstX == row.length) {
|
||||||
dstX = 0;
|
dstX = 0;
|
||||||
@ -120,7 +120,7 @@ abstract class AbstractRLEDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return decoded;
|
return pBuffer.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,9 +28,9 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.*;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code Decoder} implementation for standard base64 encoding.
|
* {@code Decoder} implementation for standard base64 encoding.
|
||||||
@ -62,8 +62,6 @@ public final class Base64Decoder implements Decoder {
|
|||||||
final static byte[] PEM_CONVERT_ARRAY;
|
final static byte[] PEM_CONVERT_ARRAY;
|
||||||
|
|
||||||
private byte[] decodeBuffer = new byte[4];
|
private byte[] decodeBuffer = new byte[4];
|
||||||
private ByteArrayOutputStream wrapped;
|
|
||||||
private Object wrappedObject;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PEM_CONVERT_ARRAY = new byte[256];
|
PEM_CONVERT_ARRAY = new byte[256];
|
||||||
@ -93,7 +91,7 @@ public final class Base64Decoder implements Decoder {
|
|||||||
return pLength;
|
return pLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean decodeAtom(final InputStream pInput, final OutputStream pOutput, final int pLength)
|
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
byte byte0 = -1;
|
byte byte0 = -1;
|
||||||
@ -147,16 +145,16 @@ public final class Base64Decoder implements Decoder {
|
|||||||
default:
|
default:
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 2:
|
case 2:
|
||||||
pOutput.write((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
pOutput.write((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||||
pOutput.write((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
pOutput.write((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
|
||||||
pOutput.write((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
|
||||||
pOutput.write((byte) (byte2 << 6 & 192 | byte3 & 63));
|
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,34 +164,23 @@ public final class Base64Decoder implements Decoder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void decodeBuffer(final InputStream pInput, final ByteArrayOutputStream pOutput, final int pLength) throws IOException {
|
public int decode(final InputStream pStream, final ByteBuffer pBuffer) throws IOException {
|
||||||
do {
|
do {
|
||||||
int k = 72;
|
int k = 72;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i + 4 < k; i += 4) {
|
for (i = 0; i + 4 < k; i += 4) {
|
||||||
if(!decodeAtom(pInput, pOutput, 4)) {
|
if(!decodeAtom(pStream, pBuffer, 4)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decodeAtom(pInput, pOutput, k - i)) {
|
if (!decodeAtom(pStream, pBuffer, k - i)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (pOutput.size() + 54 < pLength); // 72 char lines should produce no more than 54 bytes
|
while (pBuffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
|
||||||
}
|
|
||||||
|
|
||||||
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
return pBuffer.position();
|
||||||
if (wrappedObject != pBuffer) {
|
|
||||||
// NOTE: Array not cloned in FastByteArrayOutputStream
|
|
||||||
wrapped = new FastByteArrayOutputStream(pBuffer);
|
|
||||||
wrappedObject = pBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapped.reset(); // NOTE: This only resets count to 0
|
|
||||||
decodeBuffer(pStream, wrapped, pBuffer.length);
|
|
||||||
|
|
||||||
return wrapped.size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,9 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for decoders.
|
* Interface for decoders.
|
||||||
@ -60,5 +61,5 @@ public interface Decoder {
|
|||||||
* @throws IOException if an I/O error occurs
|
* @throws IOException if an I/O error occurs
|
||||||
* @throws java.io.EOFException if a premature end-of-file is encountered
|
* @throws java.io.EOFException if a premature end-of-file is encountered
|
||||||
*/
|
*/
|
||||||
int decode(InputStream pStream, byte[] pBuffer) throws IOException;
|
int decode(InputStream pStream, ByteBuffer pBuffer) throws IOException;
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,10 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code InputStream} that provides on-the-fly decoding from an underlying
|
* An {@code InputStream} that provides on-the-fly decoding from an underlying
|
||||||
@ -43,12 +44,12 @@ import java.io.FilterInputStream;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public final class DecoderStream extends FilterInputStream {
|
public final class DecoderStream extends FilterInputStream {
|
||||||
|
protected final ByteBuffer buffer;
|
||||||
protected int bufferPos;
|
|
||||||
protected int bufferLimit;
|
|
||||||
protected final byte[] buffer;
|
|
||||||
protected final Decoder decoder;
|
protected final Decoder decoder;
|
||||||
|
|
||||||
|
// TODO: Consider replacing the wrapped input stream with a channel like this
|
||||||
|
// ReadableByteChannel inChannel = Channels.newChannel(stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new decoder stream and chains it to the
|
* Creates a new decoder stream and chains it to the
|
||||||
* input stream specified by the {@code pStream} argument.
|
* input stream specified by the {@code pStream} argument.
|
||||||
@ -77,29 +78,22 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
||||||
super(pStream);
|
super(pStream);
|
||||||
decoder = pDecoder;
|
decoder = pDecoder;
|
||||||
buffer = new byte[pBufferSize];
|
buffer = ByteBuffer.allocate(pBufferSize);
|
||||||
bufferPos = 0;
|
buffer.flip();
|
||||||
bufferLimit = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
return bufferLimit - bufferPos + super.available();
|
return buffer.remaining();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
if (bufferPos == bufferLimit) {
|
if (!buffer.hasRemaining()) {
|
||||||
bufferLimit = fill();
|
if (fill() < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufferLimit < 0) {
|
return buffer.get() & 0xff;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer[bufferPos++] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int read(final byte pBytes[]) throws IOException {
|
|
||||||
return read(pBytes, 0, pBytes.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
||||||
@ -115,8 +109,10 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// End of file?
|
// End of file?
|
||||||
if ((bufferLimit - bufferPos) < 0) {
|
if (!buffer.hasRemaining()) {
|
||||||
return -1;
|
if (fill() < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read until we have read pLength bytes, or have reached EOF
|
// Read until we have read pLength bytes, or have reached EOF
|
||||||
@ -124,21 +120,15 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
int off = pOffset;
|
int off = pOffset;
|
||||||
|
|
||||||
while (pLength > count) {
|
while (pLength > count) {
|
||||||
int avail = bufferLimit - bufferPos;
|
if (!buffer.hasRemaining()) {
|
||||||
|
if (fill() < 0) {
|
||||||
if (avail <= 0) {
|
|
||||||
bufferLimit = fill();
|
|
||||||
|
|
||||||
if (bufferLimit < 0) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy as many bytes as possible
|
// Copy as many bytes as possible
|
||||||
int dstLen = Math.min(pLength - count, avail);
|
int dstLen = Math.min(pLength - count, buffer.remaining());
|
||||||
System.arraycopy(buffer, bufferPos, pBytes, off, dstLen);
|
buffer.get(pBytes, off, dstLen);
|
||||||
|
|
||||||
bufferPos += dstLen;
|
|
||||||
|
|
||||||
// Update offset (rest)
|
// Update offset (rest)
|
||||||
off += dstLen;
|
off += dstLen;
|
||||||
@ -152,29 +142,25 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
|
|
||||||
public long skip(final long pLength) throws IOException {
|
public long skip(final long pLength) throws IOException {
|
||||||
// End of file?
|
// End of file?
|
||||||
if (bufferLimit - bufferPos < 0) {
|
if (!buffer.hasRemaining()) {
|
||||||
return 0;
|
if (fill() < 0) {
|
||||||
|
return 0; // Yes, 0, not -1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip until we have skipped pLength bytes, or have reached EOF
|
// Skip until we have skipped pLength bytes, or have reached EOF
|
||||||
long total = 0;
|
long total = 0;
|
||||||
|
|
||||||
while (total < pLength) {
|
while (total < pLength) {
|
||||||
int avail = bufferLimit - bufferPos;
|
if (!buffer.hasRemaining()) {
|
||||||
|
if (fill() < 0) {
|
||||||
if (avail == 0) {
|
|
||||||
bufferLimit = fill();
|
|
||||||
|
|
||||||
if (bufferLimit < 0) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Skipped can never be more than avail, which is
|
// NOTE: Skipped can never be more than avail, which is
|
||||||
// an int, so the cast is safe
|
// an int, so the cast is safe
|
||||||
int skipped = (int) Math.min(pLength - total, avail);
|
int skipped = (int) Math.min(pLength - total, buffer.remaining());
|
||||||
|
|
||||||
bufferPos += skipped; // Just skip these bytes
|
|
||||||
total += skipped;
|
total += skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,19 +176,20 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
* @throws IOException if an I/O error occurs
|
* @throws IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
protected int fill() throws IOException {
|
protected int fill() throws IOException {
|
||||||
|
buffer.clear();
|
||||||
int read = decoder.decode(in, buffer);
|
int read = decoder.decode(in, buffer);
|
||||||
|
|
||||||
// TODO: Enforce this in test case, leave here to aid debugging
|
// TODO: Enforce this in test case, leave here to aid debugging
|
||||||
if (read > buffer.length) {
|
if (read > buffer.capacity()) {
|
||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
String.format(
|
String.format(
|
||||||
"Decode beyond buffer (%d): %d (using %s decoder)",
|
"Decode beyond buffer (%d): %d (using %s decoder)",
|
||||||
buffer.length, read, decoder.getClass().getName()
|
buffer.capacity(), read, decoder.getClass().getName()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferPos = 0;
|
buffer.flip();
|
||||||
|
|
||||||
if (read == 0) {
|
if (read == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -28,9 +28,10 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decoder implementation for 16 bit-chunked Apple PackBits-like run-length
|
* Decoder implementation for 16 bit-chunked Apple PackBits-like run-length
|
||||||
@ -84,13 +85,13 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
*
|
*
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
*/
|
*/
|
||||||
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
public int decode(final InputStream pStream, final ByteBuffer pBuffer) throws IOException {
|
||||||
if (reachedEOF) {
|
if (reachedEOF) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read = 0;
|
int read = 0;
|
||||||
final int max = pBuffer.length;
|
final int max = pBuffer.capacity();
|
||||||
|
|
||||||
while (read < max) {
|
while (read < max) {
|
||||||
int n;
|
int n;
|
||||||
@ -126,7 +127,7 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
if (n >= 0) {
|
if (n >= 0) {
|
||||||
// Copy next n + 1 shorts literally
|
// Copy next n + 1 shorts literally
|
||||||
int len = 2 * (n + 1);
|
int len = 2 * (n + 1);
|
||||||
readFully(pStream, pBuffer, read, len);
|
readFully(pStream, pBuffer, len);
|
||||||
read += len;
|
read += len;
|
||||||
}
|
}
|
||||||
// Allow -128 for compatibility, see above
|
// Allow -128 for compatibility, see above
|
||||||
@ -136,8 +137,8 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
byte value2 = readByte(pStream);
|
byte value2 = readByte(pStream);
|
||||||
|
|
||||||
for (int i = -n + 1; i > 0; i--) {
|
for (int i = -n + 1; i > 0; i--) {
|
||||||
pBuffer[read++] = value1;
|
pBuffer.put(value1);
|
||||||
pBuffer[read++] = value2;
|
pBuffer.put(value2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else NOOP (-128)
|
// else NOOP (-128)
|
||||||
@ -160,7 +161,7 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
return (byte) read;
|
return (byte) read;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void readFully(final InputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
private static void readFully(final InputStream pStream, final ByteBuffer pBuffer, final int pLength) throws IOException {
|
||||||
if (pLength < 0) {
|
if (pLength < 0) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
@ -168,7 +169,7 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
int read = 0;
|
int read = 0;
|
||||||
|
|
||||||
while (read < pLength) {
|
while (read < pLength) {
|
||||||
int count = pStream.read(pBuffer, pOffset + read, pLength - read);
|
int count = pStream.read(pBuffer.array(), pBuffer.arrayOffset() + pBuffer.position() + read, pLength - read);
|
||||||
|
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
throw new EOFException("Unexpected end of PackBits stream");
|
throw new EOFException("Unexpected end of PackBits stream");
|
||||||
|
@ -31,6 +31,7 @@ package com.twelvemonkeys.io.enc;
|
|||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decoder implementation for Apple PackBits run-length encoding.
|
* Decoder implementation for Apple PackBits run-length encoding.
|
||||||
@ -98,16 +99,13 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
*
|
*
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
*/
|
*/
|
||||||
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
public int decode(final InputStream pStream, final ByteBuffer pBuffer) throws IOException {
|
||||||
if (reachedEOF) {
|
if (reachedEOF) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read = 0;
|
|
||||||
final int max = pBuffer.length;
|
|
||||||
|
|
||||||
// TODO: Don't decode more than single runs, because some writers add pad bytes inside the stream...
|
// TODO: Don't decode more than single runs, because some writers add pad bytes inside the stream...
|
||||||
while (read < max) {
|
while (pBuffer.hasRemaining()) {
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (splitRun) {
|
if (splitRun) {
|
||||||
@ -126,12 +124,12 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Split run at or before max
|
// Split run at or before max
|
||||||
if (n >= 0 && n + 1 + read > max) {
|
if (n >= 0 && n + 1 > pBuffer.remaining()) {
|
||||||
leftOfRun = n;
|
leftOfRun = n;
|
||||||
splitRun = true;
|
splitRun = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (n < 0 && -n + 1 + read > max) {
|
else if (n < 0 && -n + 1 > pBuffer.remaining()) {
|
||||||
leftOfRun = n;
|
leftOfRun = n;
|
||||||
splitRun = true;
|
splitRun = true;
|
||||||
break;
|
break;
|
||||||
@ -140,9 +138,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
try {
|
try {
|
||||||
if (n >= 0) {
|
if (n >= 0) {
|
||||||
// Copy next n + 1 bytes literally
|
// Copy next n + 1 bytes literally
|
||||||
readFully(pStream, pBuffer, read, n + 1);
|
readFully(pStream, pBuffer, n + 1);
|
||||||
|
|
||||||
read += n + 1;
|
|
||||||
}
|
}
|
||||||
// Allow -128 for compatibility, see above
|
// Allow -128 for compatibility, see above
|
||||||
else if (disableNoop || n != -128) {
|
else if (disableNoop || n != -128) {
|
||||||
@ -150,7 +146,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
byte value = readByte(pStream);
|
byte value = readByte(pStream);
|
||||||
|
|
||||||
for (int i = -n + 1; i > 0; i--) {
|
for (int i = -n + 1; i > 0; i--) {
|
||||||
pBuffer[read++] = value;
|
pBuffer.put(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else NOOP (-128)
|
// else NOOP (-128)
|
||||||
@ -160,7 +156,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return read;
|
return pBuffer.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte readByte(final InputStream pStream) throws IOException {
|
static byte readByte(final InputStream pStream) throws IOException {
|
||||||
@ -173,7 +169,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
return (byte) read;
|
return (byte) read;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readFully(final InputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
static void readFully(final InputStream pStream, final ByteBuffer pBuffer, final int pLength) throws IOException {
|
||||||
if (pLength < 0) {
|
if (pLength < 0) {
|
||||||
throw new IndexOutOfBoundsException(String.format("Negative length: %d", pLength));
|
throw new IndexOutOfBoundsException(String.format("Negative length: %d", pLength));
|
||||||
}
|
}
|
||||||
@ -181,7 +177,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
while (total < pLength) {
|
while (total < pLength) {
|
||||||
int count = pStream.read(pBuffer, pOffset + total, pLength - total);
|
int count = pStream.read(pBuffer.array(), pBuffer.arrayOffset() + pBuffer.position() + total, pLength - total);
|
||||||
|
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
throw new EOFException("Unexpected end of PackBits stream");
|
throw new EOFException("Unexpected end of PackBits stream");
|
||||||
@ -189,5 +185,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
|
|
||||||
total += count;
|
total += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pBuffer.position(pBuffer.position() + total);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ public abstract class DecoderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[0]);
|
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[0]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int count = decoder.decode(bytes, new byte[128]);
|
int count = decoder.decode(bytes, ByteBuffer.allocate(128));
|
||||||
assertEquals("Should not be able to read any bytes", 0, count);
|
assertEquals("Should not be able to read any bytes", 0, count);
|
||||||
}
|
}
|
||||||
catch (EOFException allowed) {
|
catch (EOFException allowed) {
|
||||||
|
@ -33,6 +33,7 @@ import com.twelvemonkeys.io.enc.Decoder;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lempel–Ziv–Welch (LZW) decompression. LZW is a universal loss-less data compression algorithm
|
* Lempel–Ziv–Welch (LZW) decompression. LZW is a universal loss-less data compression algorithm
|
||||||
@ -94,10 +95,9 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
maxString = 1;
|
maxString = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int decode(final InputStream stream, final byte[] buffer) throws IOException {
|
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||||
// Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992.
|
// Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992.
|
||||||
// See Section 13: "LZW Compression"/"LZW Decoding", page 61+
|
// See Section 13: "LZW Compression"/"LZW Decoding", page 61+
|
||||||
int bufferPos = 0;
|
|
||||||
int code;
|
int code;
|
||||||
|
|
||||||
while ((code = getNextCode(stream)) != EOI_CODE) {
|
while ((code = getNextCode(stream)) != EOI_CODE) {
|
||||||
@ -109,30 +109,30 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferPos += table[code].writeTo(buffer, bufferPos);
|
table[code].writeTo(buffer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (isInTable(code)) {
|
if (isInTable(code)) {
|
||||||
bufferPos += table[code].writeTo(buffer, bufferPos);
|
table[code].writeTo(buffer);
|
||||||
addStringToTable(table[oldCode].concatenate(table[code].firstChar));
|
addStringToTable(table[oldCode].concatenate(table[code].firstChar));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String outString = table[oldCode].concatenate(table[oldCode].firstChar);
|
String outString = table[oldCode].concatenate(table[oldCode].firstChar);
|
||||||
|
|
||||||
bufferPos += outString.writeTo(buffer, bufferPos);
|
outString.writeTo(buffer);
|
||||||
addStringToTable(outString);
|
addStringToTable(outString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oldCode = code;
|
oldCode = code;
|
||||||
|
|
||||||
if (bufferPos >= buffer.length - maxString - 1) {
|
if (buffer.remaining() < maxString + 1) {
|
||||||
// Buffer full, stop decoding for now
|
// Buffer full, stop decoding for now
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bufferPos;
|
return buffer.position();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStringToTable(final String string) throws IOException {
|
private void addStringToTable(final String string) throws IOException {
|
||||||
@ -301,24 +301,24 @@ abstract class LZWDecoder implements Decoder {
|
|||||||
return new String(firstChar, this.firstChar, length + 1, this);
|
return new String(firstChar, this.firstChar, length + 1, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int writeTo(final byte[] buffer, final int offset) {
|
public final void writeTo(final ByteBuffer buffer) {
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
else if (length == 1) {
|
|
||||||
buffer[offset] = value;
|
|
||||||
|
|
||||||
return 1;
|
if (length == 1) {
|
||||||
|
buffer.put(value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String e = this;
|
String e = this;
|
||||||
|
final int offset = buffer.position();
|
||||||
|
|
||||||
for (int i = length - 1; i >= 0; i--) {
|
for (int i = length - 1; i >= 0; i--) {
|
||||||
buffer[offset + i] = e.value;
|
buffer.put(offset + i, e.value);
|
||||||
e = e.previous;
|
e = e.previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
buffer.position(offset + length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ package com.twelvemonkeys.io.enc;
|
|||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
@ -75,11 +76,11 @@ final class InflateDecoder implements Decoder {
|
|||||||
buffer = new byte[1024];
|
buffer = new byte[1024];
|
||||||
}
|
}
|
||||||
|
|
||||||
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
public int decode(final InputStream pStream, final ByteBuffer pBuffer) throws IOException {
|
||||||
try {
|
try {
|
||||||
int decoded;
|
int decoded;
|
||||||
|
|
||||||
while ((decoded = inflater.inflate(pBuffer, 0, pBuffer.length)) == 0) {
|
while ((decoded = inflater.inflate(pBuffer.array(), pBuffer.arrayOffset(), pBuffer.capacity())) == 0) {
|
||||||
if (inflater.finished() || inflater.needsDictionary()) {
|
if (inflater.finished() || inflater.needsDictionary()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user