TMC-IOENC-11: Fixed problem introduced when migrating byte[] -> ByteBuffer

This commit is contained in:
Harald Kuhr 2013-09-27 14:21:31 +02:00
parent cd197afc04
commit 1acc04eeaf
5 changed files with 33 additions and 22 deletions

View File

@ -33,9 +33,9 @@ import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* Interface for endcoders. * Interface for encoders.
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform * An {@code Encoder} may be used with an {@code EncoderStream}, to perform
* on-the-fly enoding to an {@code OutputStream}. * on-the-fly encoding to an {@code OutputStream}.
* <p/> * <p/>
* Important note: Encoder implementations are typically not synchronized. * Important note: Encoder implementations are typically not synchronized.
* *
@ -48,7 +48,7 @@ import java.nio.ByteBuffer;
public interface Encoder { public interface Encoder {
/** /**
* Encodes up to {@code pBuffer.length} bytes into the given input stream, * Encodes up to {@code buffer.remaining()} bytes into the given input stream,
* from the given buffer. * from the given buffer.
* *
* @param stream the output stream to encode data to * @param stream the output stream to encode data to

View File

@ -44,6 +44,7 @@ import java.nio.ByteBuffer;
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $
*/ */
public final class EncoderStream extends FilterOutputStream { public final class EncoderStream extends FilterOutputStream {
// TODO: This class need a test case ASAP!!!
protected final Encoder encoder; protected final Encoder encoder;
private final boolean flushOnWrite; private final boolean flushOnWrite;
@ -91,7 +92,9 @@ public final class EncoderStream extends FilterOutputStream {
} }
private void encodeBuffer() throws IOException { private void encodeBuffer() throws IOException {
if (buffer.hasRemaining()) { if (buffer.position() != 0) {
buffer.flip();
// Make sure all remaining data in buffer is written to the stream // Make sure all remaining data in buffer is written to the stream
encoder.encode(out, buffer); encoder.encode(out, buffer);

View File

@ -73,53 +73,57 @@ public final class PackBitsEncoder implements Encoder {
} }
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException { public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
buffer.position(buffer.remaining());
}
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
// NOTE: It's best to encode a 2 byte repeat // NOTE: It's best to encode a 2 byte repeat
// run as a replicate run except when preceded and followed by a // run as a replicate run except when preceded and followed by a
// literal run, in which case it's best to merge the three into one // literal run, in which case it's best to merge the three into one
// literal run. Always encode 3 byte repeats as replicate runs. // literal run. Always encode 3 byte repeats as replicate runs.
// NOTE: Worst case: output = input + (input + 127) / 128 // NOTE: Worst case: output = input + (input + 127) / 128
int offset = buffer.position(); int offset = pOffset;
final int max = buffer.remaining() - 1; final int max = pOffset + pLength - 1;
final int maxMinus1 = max - 1; final int maxMinus1 = max - 1;
final byte[] pBuffer = buffer.array();
while (offset <= max) { while (offset <= max) {
// Compressed run // Compressed run
int run = 1; int run = 1;
byte replicate = pBuffer[offset]; byte replicate = pBuffer[offset];
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) { while(run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
offset++; offset++;
run++; run++;
} }
if (run > 1) { if (run > 1) {
offset++; offset++;
stream.write(-(run - 1)); pStream.write(-(run - 1));
stream.write(replicate); pStream.write(replicate);
} }
// Literal run // Literal run
run = 0; run = 0;
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1]) while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) { || (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
this.buffer[run++] = pBuffer[offset++]; buffer[run++] = pBuffer[offset++];
} }
// If last byte, include it in literal run, if space // If last byte, include it in literal run, if space
if (offset == max && run > 0 && run < 128) { if (offset == max && run > 0 && run < 128) {
this.buffer[run++] = pBuffer[offset++]; buffer[run++] = pBuffer[offset++];
} }
if (run > 0) { if (run > 0) {
stream.write(run - 1); pStream.write(run - 1);
stream.write(this.buffer, 0, run); pStream.write(buffer, 0, run);
} }
// If last byte, and not space, start new literal run // If last byte, and not space, start new literal run
if (offset == max && (run <= 0 || run >= 128)) { if (offset == max && (run <= 0 || run >= 128)) {
stream.write(0); pStream.write(0);
stream.write(pBuffer[offset++]); pStream.write(pBuffer[offset++]);
} }
} }
} }

View File

@ -54,7 +54,12 @@ public abstract class EncoderAbstractTestCase extends ObjectAbstractTestCase {
OutputStream out = new EncoderStream(outBytes, createEncoder(), true); OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
try { try {
out.write(data); // Provoke failure for encoders that doesn't take array offset properly into account
int off = (data.length + 1) / 2;
out.write(data, 0, off);
if (data.length > off) {
out.write(data, off, data.length - off);
}
} }
finally { finally {
out.close(); out.close();
@ -127,4 +132,8 @@ public abstract class EncoderAbstractTestCase extends ObjectAbstractTestCase {
} }
} }
} }
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
} }

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.Encoder;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
import com.twelvemonkeys.io.enc.PackBitsEncoder;
/** /**
* PackBitsEncoderTest * PackBitsEncoderTest
* <p/> * <p/>