mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 02:55:28 -04:00
TMC-IOENC-11: Fixed problem introduced when migrating byte[] -> ByteBuffer
This commit is contained in:
parent
cd197afc04
commit
1acc04eeaf
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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++]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user