- Moved unused encoders/decoders to sandbox

- Fixed Base64 encoder/decoder to pass test cases
- Minor clean-up in other decoders
This commit is contained in:
Harald Kuhr 2009-09-18 22:46:31 +02:00
parent 40a5960a9f
commit c45882846a
20 changed files with 259 additions and 139 deletions

View File

@ -33,7 +33,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* Abstract base class for RLE decoding as specifed 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.
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
@ -59,9 +59,11 @@ abstract class AbstractRLEDecoder implements Decoder {
mWidth = pWidth; mWidth = pWidth;
int bytesPerRow = mWidth; int bytesPerRow = mWidth;
int mod = bytesPerRow % 4; int mod = bytesPerRow % 4;
if (mod != 0) { if (mod != 0) {
bytesPerRow += 4 - mod; bytesPerRow += 4 - mod;
} }
mRow = new byte[bytesPerRow]; mRow = new byte[bytesPerRow];
mSrcX = 0; mSrcX = 0;
@ -133,6 +135,7 @@ abstract class AbstractRLEDecoder implements Decoder {
if (pByte < 0) { if (pByte < 0) {
throw new EOFException("Premature end of file"); throw new EOFException("Premature end of file");
} }
return pByte; return pByte;
} }
} }

View File

@ -43,7 +43,7 @@ import java.io.*;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java#2 $
*/ */
public class Base64Decoder implements Decoder { public final class Base64Decoder implements Decoder {
/** /**
* This array maps the characters to their 6 bit values * This array maps the characters to their 6 bit values
*/ */
@ -60,12 +60,14 @@ public class Base64Decoder implements Decoder {
}; };
final static byte[] PEM_CONVERT_ARRAY; final static byte[] PEM_CONVERT_ARRAY;
private byte[] decode_buffer = new byte[4];
private byte[] mDecodeBuffer = new byte[4];
private ByteArrayOutputStream mWrapped; private ByteArrayOutputStream mWrapped;
private Object mWrappedObject; private Object mWrappedObject;
static { static {
PEM_CONVERT_ARRAY = new byte[256]; PEM_CONVERT_ARRAY = new byte[256];
for (int i = 0; i < 255; i++) { for (int i = 0; i < 255; i++) {
PEM_CONVERT_ARRAY[i] = -1; PEM_CONVERT_ARRAY[i] = -1;
} }
@ -75,20 +77,23 @@ public class Base64Decoder implements Decoder {
} }
} }
protected static int readFully(InputStream pStream, byte pBytes[], protected static int readFully(final InputStream pStream, final byte pBytes[], final int pOffset, final int pLength)
int pOffset, int pLength) throws IOException { throws IOException
{
for (int i = 0; i < pLength; i++) { for (int i = 0; i < pLength; i++) {
int read = pStream.read(); int read = pStream.read();
if (read == -1) { if (read == -1) {
return i != 0 ? i : -1; return i != 0 ? i : -1;
} }
pBytes[i + pOffset] = (byte) read; pBytes[i + pOffset] = (byte) read;
} }
return pLength; return pLength;
} }
protected boolean decodeAtom(InputStream pInput, OutputStream pOutput, int pLength) protected boolean decodeAtom(final InputStream pInput, final OutputStream pOutput, final int pLength)
throws IOException { throws IOException {
byte byte0 = -1; byte byte0 = -1;
@ -102,40 +107,45 @@ public class Base64Decoder implements Decoder {
int read; int read;
// Skip linefeeds // Skip line feeds
do { do {
read = pInput.read(); read = pInput.read();
if (read == -1) { if (read == -1) {
return false; return false;
} }
} while (read == 10 || read == 13); } while (read == 10 || read == 13);
decode_buffer[0] = (byte) read; mDecodeBuffer[0] = (byte) read;
read = readFully(pInput, decode_buffer, 1, pLength - 1); read = readFully(pInput, mDecodeBuffer, 1, pLength - 1);
if (read == -1) { if (read == -1) {
return false; return false;
} }
if (pLength > 3 && decode_buffer[3] == 61) {
pLength = 3; int length = pLength;
}
if (pLength > 2 && decode_buffer[2] == 61) { if (length > 3 && mDecodeBuffer[3] == 61) {
pLength = 2; length = 3;
} }
switch (pLength) { if (length > 2 && mDecodeBuffer[2] == 61) {
length = 2;
}
switch (length) {
case 4: case 4:
byte3 = PEM_CONVERT_ARRAY[decode_buffer[3] & 255]; byte3 = PEM_CONVERT_ARRAY[mDecodeBuffer[3] & 255];
// fall through // fall through
case 3: case 3:
byte2 = PEM_CONVERT_ARRAY[decode_buffer[2] & 255]; byte2 = PEM_CONVERT_ARRAY[mDecodeBuffer[2] & 255];
// fall through // fall through
case 2: case 2:
byte1 = PEM_CONVERT_ARRAY[decode_buffer[1] & 255]; byte1 = PEM_CONVERT_ARRAY[mDecodeBuffer[1] & 255];
byte0 = PEM_CONVERT_ARRAY[decode_buffer[0] & 255]; byte0 = PEM_CONVERT_ARRAY[mDecodeBuffer[0] & 255];
// fall through // fall through
default: default:
switch (pLength) { switch (length) {
case 2: case 2:
pOutput.write((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3)); pOutput.write((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
break; break;
@ -149,16 +159,18 @@ public class Base64Decoder implements Decoder {
pOutput.write((byte) (byte2 << 6 & 192 | byte3 & 63)); pOutput.write((byte) (byte2 << 6 & 192 | byte3 & 63));
break; break;
} }
break; break;
} }
return true; return true;
} }
void decodeBuffer(InputStream pInput, ByteArrayOutputStream pOutput, int pLength) throws IOException { void decodeBuffer(final InputStream pInput, final ByteArrayOutputStream pOutput, final int pLength) 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(pInput, pOutput, 4)) {
break; break;
@ -169,17 +181,17 @@ public class Base64Decoder implements Decoder {
break; break;
} }
} }
while (true); while (pOutput.size() + 54 < pLength); // 72 char lines should produce no more than 54 bytes
} }
public int decode(InputStream pStream, byte[] pBuffer) throws IOException { public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
if (mWrappedObject != pBuffer) { if (mWrappedObject != pBuffer) {
// NOTE: Array not cloned in FastByteArrayOutputStream // NOTE: Array not cloned in FastByteArrayOutputStream
mWrapped = new FastByteArrayOutputStream(pBuffer); mWrapped = new FastByteArrayOutputStream(pBuffer);
mWrappedObject = pBuffer; mWrappedObject = pBuffer;
} }
mWrapped.reset(); // NOTE: This only resets count to 0
mWrapped.reset(); // NOTE: This only resets count to 0
decodeBuffer(pStream, mWrapped, pBuffer.length); decodeBuffer(pStream, mWrapped, pBuffer.length);
return mWrapped.size(); return mWrapped.size();

View File

@ -44,26 +44,37 @@ import java.io.IOException;
*/ */
public class Base64Encoder implements Encoder { public class Base64Encoder implements Encoder {
public void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException { public void encode(final OutputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength)
throws IOException
{
if (pOffset < 0 || pOffset > pLength || pOffset > pBuffer.length) {
throw new IndexOutOfBoundsException("offset outside [0...length]");
}
else if (pLength > pBuffer.length) {
throw new IndexOutOfBoundsException("length > buffer length");
}
// TODO: Implement // TODO: Implement
// NOTE: This is impossible, given the current spec, as we need to either: // NOTE: This is impossible, given the current spec, as we need to either:
// - buffer all data in the EncoderStream // - buffer all data in the EncoderStream
// - or have flush/end method(s) in the Encoder // - or have flush/end method(s) in the Encoder
// to ensure proper end of stream handling // to ensure proper end of stream handling
int length = pLength;
int offset = pOffset; int offset = pOffset;
// TODO: Temp impl, will only work for single writes // TODO: Temp impl, will only work for single writes
while ((pBuffer.length - offset) > 0) { while ((pBuffer.length - offset) > 0) {
byte a, b, c; byte a, b, c;
if ((pBuffer.length - offset) > 2) { if ((pBuffer.length - offset) > 2) {
pLength = 3; length = 3;
} }
else { else {
pLength = pBuffer.length - offset; length = pBuffer.length - offset;
} }
switch (pLength) { switch (length) {
case 1: case 1:
a = pBuffer[offset]; a = pBuffer[offset];
b = 0; b = 0;

View File

@ -33,7 +33,7 @@ import java.io.IOException;
import java.io.FilterInputStream; import java.io.FilterInputStream;
/** /**
* An {@code InputStream} that provides on-the-fly deoding from an underlying * An {@code InputStream} that provides on-the-fly decoding from an underlying
* stream. * stream.
* <p/> * <p/>
* @see EncoderStream * @see EncoderStream
@ -54,11 +54,11 @@ public final class DecoderStream extends FilterInputStream {
* input stream specified by the {@code pStream} argument. * input stream specified by the {@code pStream} argument.
* *
* @param pStream the underlying input stream. * @param pStream the underlying input stream.
* @param pDecoder * @param pDecoder the decoder that will be used to decode the underlying stream
* *
* @see java.io.FilterInputStream#in * @see java.io.FilterInputStream#in
*/ */
public DecoderStream(InputStream pStream, Decoder pDecoder) { public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
super(pStream); super(pStream);
mDecoder = pDecoder; mDecoder = pDecoder;
mBuffer = new byte[1024]; mBuffer = new byte[1024];
@ -92,7 +92,7 @@ public final class DecoderStream extends FilterInputStream {
} }
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) || else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) { ((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " lenght=" + pLength); throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
} }
else if (pLength == 0) { else if (pLength == 0) {
return 0; return 0;
@ -106,6 +106,7 @@ public final class DecoderStream extends FilterInputStream {
// Read until we have read pLength bytes, or have reached EOF // Read until we have read pLength bytes, or have reached EOF
int count = 0; int count = 0;
int off = pOffset; int off = pOffset;
while (pLength > count) { while (pLength > count) {
int avail = mBufferLimit - mBufferPos; int avail = mBufferLimit - mBufferPos;
@ -120,6 +121,7 @@ public final class DecoderStream extends FilterInputStream {
// 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, avail);
System.arraycopy(mBuffer, mBufferPos, pBytes, off, dstLen); System.arraycopy(mBuffer, mBufferPos, pBytes, off, dstLen);
mBufferPos += dstLen; mBufferPos += dstLen;
// Update offset (rest) // Update offset (rest)
@ -129,13 +131,6 @@ public final class DecoderStream extends FilterInputStream {
count += dstLen; count += dstLen;
} }
/*
for (int i = 0; i < count; i++) {
byte b = pBytes[pOffset + i];
System.out.print("0x" + Integer.toHexString(b & 0xff));
}
*/
return count; return count;
} }
@ -179,11 +174,18 @@ public final class DecoderStream extends FilterInputStream {
*/ */
protected int fill() throws IOException { protected int fill() throws IOException {
int read = mDecoder.decode(in, mBuffer); int read = mDecoder.decode(in, mBuffer);
// TODO: Enforce this in test case, leave here to aid debugging
if (read > mBuffer.length) {
throw new AssertionError(String.format("Decode beyond buffer (%d): %d", mBuffer.length, read));
}
mBufferPos = 0; mBufferPos = 0;
if (read == 0) { if (read == 0) {
return -1; return -1;
} }
return read; return read;
} }
} }

View File

@ -47,7 +47,7 @@ import java.io.OutputStream;
public interface Encoder { public interface Encoder {
/** /**
* Encodes up to {@code pBuffer.length} bytes into the given inputstream, * Encodes up to {@code pBuffer.length} bytes into the given input stream,
* from the given buffer. * from the given buffer.
* *
* @param pStream the outputstream to encode data to * @param pStream the outputstream to encode data to

View File

@ -55,9 +55,9 @@ public final class EncoderStream extends FilterOutputStream {
* underlying output stream. * underlying output stream.
* *
* @param pStream the underlying output stream * @param pStream the underlying output stream
* @param pEncoder * @param pEncoder the encoder to use
*/ */
public EncoderStream(OutputStream pStream, Encoder pEncoder) { public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
this(pStream, pEncoder, false); this(pStream, pEncoder, false);
} }
@ -66,11 +66,11 @@ public final class EncoderStream extends FilterOutputStream {
* underlying output stream. * underlying output stream.
* *
* @param pStream the underlying output stream * @param pStream the underlying output stream
* @param pEncoder * @param pEncoder the encoder to use
* @param pFlushOnWrite if {@code true}, calls to the byte-array * @param pFlushOnWrite if {@code true}, calls to the byte-array
* {@code write} methods will automatically flush the buffer. * {@code write} methods will automatically flush the buffer.
*/ */
public EncoderStream(OutputStream pStream, Encoder pEncoder, boolean pFlushOnWrite) { public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
super(pStream); super(pStream);
mEncoder = pEncoder; mEncoder = pEncoder;
@ -94,12 +94,13 @@ public final class EncoderStream extends FilterOutputStream {
if (mBufferPos != 0) { if (mBufferPos != 0) {
// Make sure all remaining data in buffer is written to the stream // Make sure all remaining data in buffer is written to the stream
mEncoder.encode(out, mBuffer, 0, mBufferPos); mEncoder.encode(out, mBuffer, 0, mBufferPos);
// Reset buffer // Reset buffer
mBufferPos = 0; mBufferPos = 0;
} }
} }
public final void write(byte[] pBytes) throws IOException { public final void write(final byte[] pBytes) throws IOException {
write(pBytes, 0, pBytes.length); write(pBytes, 0, pBytes.length);
} }
@ -107,14 +108,14 @@ public final class EncoderStream extends FilterOutputStream {
// TODO: We might need a way to explicitly flush the encoder, or specify // TODO: We might need a way to explicitly flush the encoder, or specify
// that the encoder can't buffer. In that case, the encoder should probably // that the encoder can't buffer. In that case, the encoder should probably
// tell the EncoderStream how large buffer it prefers... // tell the EncoderStream how large buffer it prefers...
public void write(byte[] pBytes, int pOffset, int pLength) throws IOException { public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (!mFlushOnWrite && mBufferPos + pLength < mBuffer.length) { if (!mFlushOnWrite && mBufferPos + pLength < mBuffer.length) {
// Buffer data // Buffer data
System.arraycopy(pBytes, pOffset, mBuffer, mBufferPos, pLength); System.arraycopy(pBytes, pOffset, mBuffer, mBufferPos, pLength);
mBufferPos += pLength; mBufferPos += pLength;
} }
else { else {
// Encode data allready in the buffer // Encode data already in the buffer
if (mBufferPos != 0) { if (mBufferPos != 0) {
encodeBuffer(); encodeBuffer();
} }
@ -124,10 +125,11 @@ public final class EncoderStream extends FilterOutputStream {
} }
} }
public void write(int pByte) throws IOException { public void write(final int pByte) throws IOException {
if (mBufferPos >= mBuffer.length - 1) { if (mBufferPos >= mBuffer.length - 1) {
encodeBuffer(); // Resets mBufferPos to 0 encodeBuffer(); // Resets mBufferPos to 0
} }
mBuffer[mBufferPos++] = (byte) pByte; mBuffer[mBufferPos++] = (byte) pByte;
} }
} }

View File

@ -62,31 +62,31 @@ public final class PackBits16Decoder implements Decoder {
/** /**
* Creates a {@code PackBitsDecoder}. * Creates a {@code PackBitsDecoder}.
* <p/> * <p/>
* As some implementations of PackBits-like encoders treat -128 as lenght of * As some implementations of PackBits-like encoders treat {@code -128} as length of
* a compressed run, instead of a no-op, it's possible to disable no-ops * a compressed run, instead of a no-op, it's possible to disable no-ops
* for compatibility. * for compatibility.
* Should be used with caution, even though, most known encoders never write * Should be used with caution, even though, most known encoders never write
* no-ops in the compressed streams. * no-ops in the compressed streams.
* *
* @param pDisableNoop * @param pDisableNoop {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
*/ */
public PackBits16Decoder(boolean pDisableNoop) { public PackBits16Decoder(final boolean pDisableNoop) {
mDisableNoop = pDisableNoop; mDisableNoop = pDisableNoop;
} }
/** /**
* Decodes bytes from the given input stream, to the given buffer. * Decodes bytes from the given input stream, to the given buffer.
* *
* @param pStream * @param pStream the stream to decode from
* @param pBuffer a byte array, minimum 128 (or 129 if no-op is disabled) * @param pBuffer a byte array, minimum 128 (or 129 if no-op is disabled)
* bytes long * bytes long
* @return The number of bytes decoded * @return The number of bytes decoded
* *
* @throws java.io.IOException * @throws java.io.IOException
*/ */
public int decode(InputStream pStream, byte[] pBuffer) throws IOException { public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
if (mEOF) { if (mEOF) {
throw new EOFException("Unexpected end of PackBits stream"); return -1;
} }
int read = 0; int read = 0;
@ -94,6 +94,7 @@ public final class PackBits16Decoder implements Decoder {
while (read < max) { while (read < max) {
int n; int n;
if (mSplitRun) { if (mSplitRun) {
// Continue run // Continue run
n = mLeftOfRun; n = mLeftOfRun;
@ -149,24 +150,30 @@ public final class PackBits16Decoder implements Decoder {
return read; return read;
} }
private static byte readByte(InputStream pStream) throws IOException { private static byte readByte(final InputStream pStream) throws IOException {
int read = pStream.read(); int read = pStream.read();
if (read < 0) { if (read < 0) {
throw new EOFException("Unexpected end of PackBits stream"); throw new EOFException("Unexpected end of PackBits stream");
} }
return (byte) read; return (byte) read;
} }
private static void readFully(InputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException { private static void readFully(final InputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
if (pLength < 0) { if (pLength < 0) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
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, pOffset + 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");
} }
read += count; read += count;
} }
} }

View File

@ -69,9 +69,7 @@ public final class PackBitsDecoder implements Decoder {
private boolean mSplitRun; private boolean mSplitRun;
private boolean mEOF; private boolean mEOF;
/** /** Creates a {@code PackBitsDecoder}. */
* Creates a {@code PackBitsDecoder}.
*/
public PackBitsDecoder() { public PackBitsDecoder() {
this(false); this(false);
} }
@ -79,31 +77,31 @@ public final class PackBitsDecoder implements Decoder {
/** /**
* Creates a {@code PackBitsDecoder}. * Creates a {@code PackBitsDecoder}.
* <p/> * <p/>
* As some implementations of PackBits-like encoders treat -128 as lenght of * As some implementations of PackBits-like encoders treat {@code -128} as length of
* a compressed run, instead of a no-op, it's possible to disable no-ops * a compressed run, instead of a no-op, it's possible to disable no-ops
* for compatibility. * for compatibility.
* Should be used with caution, even though, most known encoders never write * Should be used with caution, even though, most known encoders never write
* no-ops in the compressed streams. * no-ops in the compressed streams.
* *
* @param pDisableNoop * @param pDisableNoop {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
*/ */
public PackBitsDecoder(boolean pDisableNoop) { public PackBitsDecoder(final boolean pDisableNoop) {
mDisableNoop = pDisableNoop; mDisableNoop = pDisableNoop;
} }
/** /**
* Decodes bytes from the given input stream, to the given buffer. * Decodes bytes from the given input stream, to the given buffer.
* *
* @param pStream * @param pStream the stream to decode from
* @param pBuffer a byte array, minimum 128 (or 129 if no-op is disabled) * @param pBuffer a byte array, minimum 128 (or 129 if no-op is disabled)
* bytes long * bytes long
* @return The number of bytes decoded * @return The number of bytes decoded
* *
* @throws IOException * @throws IOException
*/ */
public int decode(InputStream pStream, byte[] pBuffer) throws IOException { public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
if (mEOF) { if (mEOF) {
throw new EOFException("Unexpected end of PackBits stream"); return -1;
} }
int read = 0; int read = 0;
@ -111,6 +109,7 @@ public final class PackBitsDecoder implements Decoder {
while (read < max) { while (read < max) {
int n; int n;
if (mSplitRun) { if (mSplitRun) {
// Continue run // Continue run
n = mLeftOfRun; n = mLeftOfRun;
@ -164,24 +163,30 @@ public final class PackBitsDecoder implements Decoder {
return read; return read;
} }
private static byte readByte(InputStream pStream) throws IOException { private static byte readByte(final InputStream pStream) throws IOException {
int read = pStream.read(); int read = pStream.read();
if (read < 0) { if (read < 0) {
throw new EOFException("Unexpected end of PackBits stream"); throw new EOFException("Unexpected end of PackBits stream");
} }
return (byte) read; return (byte) read;
} }
private static void readFully(InputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException { private static void readFully(final InputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
if (pLength < 0) { if (pLength < 0) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
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, pOffset + 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");
} }
read += count; read += count;
} }
} }

View File

@ -52,6 +52,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
while (mSrcY >= 0) { while (mSrcY >= 0) {
int byte1 = pInput.read(); int byte1 = pInput.read();
int byte2 = checkEOF(pInput.read()); int byte2 = checkEOF(pInput.read());
if (byte1 == 0x00) { if (byte1 == 0x00) {
switch (byte2) { switch (byte2) {
case 0x00: case 0x00:
@ -102,6 +103,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
mRow[mSrcX++] = (byte) byte2; mRow[mSrcX++] = (byte) byte2;
byte1 -= 2; byte1 -= 2;
} }
if (byte1 == 1) { if (byte1 == 1) {
// TODO: Half byte alignment? Seems to be ok... // TODO: Half byte alignment? Seems to be ok...
mRow[mSrcX++] = (byte) (byte2 & 0xf0); mRow[mSrcX++] = (byte) (byte2 & 0xf0);
@ -110,14 +112,15 @@ final class RLE4Decoder extends AbstractRLEDecoder {
// If we're done with a complete row, copy the data // If we're done with a complete row, copy the data
if (mSrcX == mRow.length) { if (mSrcX == mRow.length) {
// Move to new position, either absolute (delta) or next line // Move to new position, either absolute (delta) or next line
if (deltaX != 0 || deltaY != 0) { if (deltaX != 0 || deltaY != 0) {
mSrcX = (deltaX + 1) / 2; mSrcX = (deltaX + 1) / 2;
if (deltaY > mSrcY) { if (deltaY > mSrcY) {
mSrcY = deltaY; mSrcY = deltaY;
break; break;
} }
deltaX = 0; deltaX = 0;
deltaY = 0; deltaY = 0;
} }

View File

@ -52,6 +52,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
while (mSrcY >= 0) { while (mSrcY >= 0) {
int byte1 = pInput.read(); int byte1 = pInput.read();
int byte2 = checkEOF(pInput.read()); int byte2 = checkEOF(pInput.read());
if (byte1 == 0x00) { if (byte1 == 0x00) {
switch (byte2) { switch (byte2) {
case 0x00: case 0x00:

View File

@ -20,14 +20,14 @@ public class Base64EncoderTestCase extends EncoderAbstractTestCase {
} }
public void testNegativeEncode() throws IOException { public void testNegativeEncode() throws IOException {
Encoder base64 = createEncoder(); Encoder encoder = createEncoder();
ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try { try {
base64.encode(bytes, new byte[1], 2, 1); encoder.encode(bytes, new byte[1], 2, 1);
fail("wrong index should throw IndexOutOfBoundsException"); fail("wrong index should throw IndexOutOfBoundsException");
} }
catch (IndexOutOfBoundsException e) { catch (IndexOutOfBoundsException expected) {
} }
} }

View File

@ -1,11 +1,7 @@
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
import com.twelvemonkeys.io.enc.Decoder;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.Encoder;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
import java.io.*; import java.io.*;
import java.util.Arrays; import java.util.Arrays;
@ -86,14 +82,34 @@ public abstract class DecoderAbstractTestCase extends ObjectAbstractTestCase {
} }
public final void testStreams() throws Exception { public final void testStreams() throws Exception {
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; i++) {
runStreamTest(i); try {
runStreamTest(i);
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
for (int i = 100; i < 2000; i += 250) { for (int i = 100; i < 2000; i += 250) {
runStreamTest(i); try {
runStreamTest(i);
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
for (int i = 2000; i < 80000; i += 1000) { for (int i = 2000; i < 80000; i += 1000) {
runStreamTest(i); try {
runStreamTest(i);
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
} }
} }

View File

@ -1,11 +1,7 @@
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
import com.twelvemonkeys.io.enc.Decoder;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.Encoder;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
import java.io.*; import java.io.*;
import java.util.Arrays; import java.util.Arrays;
@ -31,58 +27,99 @@ public abstract class EncoderAbstractTestCase extends ObjectAbstractTestCase {
} }
public final void testNullEncode() throws IOException { public final void testNullEncode() throws IOException {
Encoder base64 = createEncoder(); Encoder encoder = createEncoder();
ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try { try {
base64.encode(bytes, null, 0, 1); encoder.encode(bytes, null, 0, 1);
fail("null should throw NullPointerException"); fail("null should throw NullPointerException");
} }
catch (NullPointerException e) { catch (NullPointerException expected) {
} }
} }
private byte[] createData(int pLength) throws Exception { private byte[] createData(final int pLength) throws Exception {
byte[] bytes = new byte[pLength]; byte[] bytes = new byte[pLength];
RANDOM.nextBytes(bytes); RANDOM.nextBytes(bytes);
return bytes; return bytes;
} }
private void runStreamTest(int pLength) throws Exception { private void runStreamTest(final int pLength) throws Exception {
byte[] data = createData(pLength); byte[] data = createData(pLength);
ByteArrayOutputStream out_bytes = new ByteArrayOutputStream(); ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
OutputStream out = new EncoderStream(out_bytes, createEncoder(), true); OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
out.write(data);
out.close(); try {
byte[] encoded = out_bytes.toByteArray(); out.write(data);
}
finally {
out.close();
}
byte[] encoded = outBytes.toByteArray();
// System.err.println("encoded.length: " + encoded.length);
// System.err.println("encoded: " + Arrays.toString(encoded));
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder())); byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
assertTrue(Arrays.equals(data, decoded)); assertTrue(Arrays.equals(data, decoded));
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()); InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
out_bytes = new ByteArrayOutputStream(); outBytes = new ByteArrayOutputStream();
/**
byte[] buffer = new byte[3];
for (int n = in.read(buffer); n > 0; n = in.read(buffer)) {
out_bytes.write(buffer, 0, n);
}
//*/
FileUtil.copy(in, out_bytes);
out_bytes.close(); try {
in.close(); FileUtil.copy(in, outBytes);
decoded = out_bytes.toByteArray(); }
finally {
outBytes.close();
in.close();
}
decoded = outBytes.toByteArray();
assertTrue(Arrays.equals(data, decoded)); assertTrue(Arrays.equals(data, decoded));
} }
public final void testStreams() throws Exception { public final void testStreams() throws Exception {
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; i++) {
runStreamTest(i); try {
runStreamTest(i);
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
for (int i = 100; i < 2000; i += 250) { for (int i = 100; i < 2000; i += 250) {
runStreamTest(i); try {
runStreamTest(i);
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
for (int i = 2000; i < 80000; i += 1000) { for (int i = 2000; i < 80000; i += 1000) {
runStreamTest(i); try {
runStreamTest(i);
}
catch (IOException e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
} }
} }
} }

View File

@ -30,12 +30,12 @@ package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.enc.DecoderStream; import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.InflateDecoder;
import com.twelvemonkeys.io.enc.PackBitsDecoder; import com.twelvemonkeys.io.enc.PackBitsDecoder;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.zip.ZipInputStream;
/** /**
* PSDUtil * PSDUtil
@ -81,7 +81,8 @@ final class PSDUtil {
} }
static DataInputStream createZipStream(final ImageInputStream pInput, int pLength) { static DataInputStream createZipStream(final ImageInputStream pInput, int pLength) {
return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new InflateDecoder())); //return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new InflateDecoder()));
return new DataInputStream(new ZipInputStream(IIOUtil.createStreamAdapter(pInput, pLength)));
} }
static DataInputStream createZipPredictorStream(final ImageInputStream pInput, int pLength) { static DataInputStream createZipPredictorStream(final ImageInputStream pInput, int pLength) {

View File

@ -36,31 +36,55 @@ import java.util.zip.Deflater;
* {@code Encoder} implementation for standard DEFLATE encoding. * {@code Encoder} implementation for standard DEFLATE encoding.
* <p/> * <p/>
* *
* @see <a href="http://tools.ietf.org/html/rfc1951">RFC 1951</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DeflateEncoder.java#2 $
* *
* @see <a href="http://tools.ietf.org/html/rfc1951">RFC 1951</a>
* @see Deflater * @see Deflater
* @see InflateDecoder * @see InflateDecoder
* @see java.util.zip.DeflaterOutputStream * @see java.util.zip.DeflaterOutputStream
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DeflateEncoder.java#2 $
*/ */
public final class DeflateEncoder implements Encoder { final class DeflateEncoder implements Encoder {
private final Deflater mDeflater; private final Deflater mDeflater;
private final byte[] mBuffer = new byte[1024];
public DeflateEncoder() { public DeflateEncoder() {
this(new Deflater()); // this(new Deflater());
this(new Deflater(Deflater.DEFAULT_COMPRESSION, true)); // TODO: Should we use "no wrap"?
} }
public DeflateEncoder(Deflater pDeflater) { public DeflateEncoder(final Deflater pDeflater) {
if (pDeflater == null) { if (pDeflater == null) {
throw new IllegalArgumentException("deflater == null"); throw new IllegalArgumentException("deflater == null");
} }
mDeflater = pDeflater; mDeflater = pDeflater;
} }
public void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException { public void encode(final OutputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength)
throw new InternalError("not implemented: encode()"); // TODO: Implement throws IOException
{
System.out.println("DeflateEncoder.encode");
mDeflater.setInput(pBuffer, pOffset, pLength);
flushInputToStream(pStream);
} }
private void flushInputToStream(final OutputStream pStream) throws IOException {
System.out.println("DeflateEncoder.flushInputToStream");
if (mDeflater.needsInput()) {
System.out.println("Foo");
}
while (!mDeflater.needsInput()) {
int deflated = mDeflater.deflate(mBuffer, 0, mBuffer.length);
pStream.write(mBuffer, 0, deflated);
System.out.println("flushed " + deflated);
}
}
// public void flush() {
// mDeflater.finish();
// }
} }

View File

@ -47,7 +47,7 @@ import java.util.zip.Inflater;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/InflateDecoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/InflateDecoder.java#2 $
*/ */
public final class InflateDecoder implements Decoder { final class InflateDecoder implements Decoder {
private final Inflater mInflater; private final Inflater mInflater;
@ -66,25 +66,29 @@ public final class InflateDecoder implements Decoder {
* *
* @param pInflater the inflater instance to use * @param pInflater the inflater instance to use
*/ */
public InflateDecoder(Inflater pInflater) { public InflateDecoder(final Inflater pInflater) {
if (pInflater == null) { if (pInflater == null) {
throw new IllegalArgumentException("inflater == null"); throw new IllegalArgumentException("inflater == null");
} }
mInflater = pInflater; mInflater = pInflater;
mBuffer = new byte[1024]; mBuffer = new byte[1024];
} }
public int decode(InputStream pStream, byte[] pBuffer) throws IOException { public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
try { try {
int decoded; int decoded;
while ((decoded = mInflater.inflate(pBuffer, 0, pBuffer.length)) == 0) { while ((decoded = mInflater.inflate(pBuffer, 0, pBuffer.length)) == 0) {
if (mInflater.finished() || mInflater.needsDictionary()) { if (mInflater.finished() || mInflater.needsDictionary()) {
return 0; return 0;
} }
if (mInflater.needsInput()) { if (mInflater.needsInput()) {
fill(pStream); fill(pStream);
} }
} }
return decoded; return decoded;
} }
catch (DataFormatException e) { catch (DataFormatException e) {
@ -93,11 +97,13 @@ public final class InflateDecoder implements Decoder {
} }
} }
private void fill(InputStream pStream) throws IOException { private void fill(final InputStream pStream) throws IOException {
int available = pStream.read(mBuffer, 0, mBuffer.length); int available = pStream.read(mBuffer, 0, mBuffer.length);
if (available == -1) { if (available == -1) {
throw new EOFException("Unexpected end of ZLIB stream"); throw new EOFException("Unexpected end of ZLIB stream");
} }
mInflater.setInput(mBuffer, 0, available); mInflater.setInput(mBuffer, 0, available);
} }
} }

View File

@ -38,7 +38,7 @@ import java.io.IOException;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/LZWDecoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/LZWDecoder.java#2 $
*/ */
public class LZWDecoder implements Decoder { final class LZWDecoder implements Decoder {
public int decode(InputStream pStream, byte[] pBuffer) throws IOException { public int decode(InputStream pStream, byte[] pBuffer) throws IOException {
return 0; // TODO: Implement return 0; // TODO: Implement
// TODO: We probably need a GIF specific subclass // TODO: We probably need a GIF specific subclass

View File

@ -38,7 +38,7 @@ import java.io.IOException;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/LZWEncoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/LZWEncoder.java#2 $
*/ */
public class LZWEncoder implements Encoder { final class LZWEncoder implements Encoder {
public void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException { public void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
// TODO: Implement // TODO: Implement
// TODO: We probably need a GIF specific subclass // TODO: We probably need a GIF specific subclass

View File

@ -1,18 +1,13 @@
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.enc.Decoder;
import com.twelvemonkeys.io.enc.DeflateEncoder;
import com.twelvemonkeys.io.enc.Encoder;
import com.twelvemonkeys.io.enc.InflateDecoder;
/** /**
* DeflateDecoderTest * DeflateEncoderTest
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/DeflateDecoderTestCase.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/enc/DeflateDecoderTestCase.java#1 $
*/ */
public class DeflateDecoderTestCase extends EncoderAbstractTestCase { public class DeflateEncoderTestCase extends EncoderAbstractTestCase {
protected Encoder createEncoder() { protected Encoder createEncoder() {
return new DeflateEncoder(); return new DeflateEncoder();
} }

View File

@ -1,10 +1,5 @@
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.enc.Decoder;
import com.twelvemonkeys.io.enc.DeflateEncoder;
import com.twelvemonkeys.io.enc.Encoder;
import com.twelvemonkeys.io.enc.InflateDecoder;
/** /**
* InflateEncoderTest * InflateEncoderTest
* <p/> * <p/>