#606: Fix bug introduced by more aggressive readDirect.

(cherry picked from commit 4e2bf131d2cf004e2f425d415df4d80f677355a6)
This commit is contained in:
Harald Kuhr 2021-04-29 20:06:36 +02:00
parent 4d45ea4966
commit 5661e7459c
2 changed files with 180 additions and 156 deletions

View File

@ -93,8 +93,8 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
@SuppressWarnings("BooleanMethodIsAlwaysInverted") @SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean fillBuffer() throws IOException { private boolean fillBuffer() throws IOException {
bufferPos = 0;
int length = raf.read(buffer, 0, buffer.length); int length = raf.read(buffer, 0, buffer.length);
bufferPos = 0;
bufferLimit = max(length, 0); bufferLimit = max(length, 0);
return bufferLimit > 0; return bufferLimit > 0;
@ -245,7 +245,7 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
// Optimized to not invalidate buffer if new position is within current buffer // Optimized to not invalidate buffer if new position is within current buffer
long newBufferPos = bufferPos + position - streamPos; long newBufferPos = bufferPos + position - streamPos;
if (newBufferPos >= 0 && newBufferPos <= bufferLimit) { if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
bufferPos = (int) newBufferPos; bufferPos = (int) newBufferPos;
} }
else { else {

View File

@ -67,8 +67,9 @@ public class BufferedFileImageInputStreamTest {
@Test @Test
public void testCreate() throws IOException { public void testCreate() throws IOException {
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp")); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"))) {
assertEquals("Data length should be same as stream length", 0, stream.length()); assertEquals("Data length should be same as stream length", 0, stream.length());
}
} }
@Test @Test
@ -104,12 +105,12 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 1024]; byte[] data = new byte[1024 * 1024];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
assertEquals("File length should be same as stream length", file.length(), stream.length()); for (byte value : data) {
assertEquals("Wrong data read", value & 0xff, stream.read());
for (byte value : data) { }
assertEquals("Wrong data read", value & 0xff, stream.read());
} }
} }
@ -118,15 +119,15 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 1024]; byte[] data = new byte[1024 * 1024];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
assertEquals("File length should be same as stream length", file.length(), stream.length()); byte[] result = new byte[1024];
byte[] result = new byte[1024]; for (int i = 0; i < data.length / result.length; i++) {
stream.readFully(result);
for (int i = 0; i < data.length / result.length; i++) { assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
stream.readFully(result); }
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
} }
} }
@ -135,16 +136,16 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 14]; byte[] data = new byte[1024 * 14];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
assertEquals("File length should be same as stream length", file.length(), stream.length()); byte[] result = new byte[7];
byte[] result = new byte[7]; for (int i = 0; i < data.length / result.length; i += 2) {
stream.readFully(result);
for (int i = 0; i < data.length / result.length; i += 2) { stream.skipBytes(result.length);
stream.readFully(result); assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
stream.skipBytes(result.length); }
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
} }
} }
@ -153,19 +154,35 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 18]; byte[] data = new byte[1024 * 18];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
assertEquals("File length should be same as stream length", file.length(), stream.length()); byte[] result = new byte[9];
byte[] result = new byte[9]; for (int i = 0; i < data.length / result.length; i++) {
// Read backwards
long newPos = stream.length() - result.length - i * result.length;
stream.seek(newPos);
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
stream.readFully(result);
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
}
}
}
for (int i = 0; i < data.length / result.length; i++) { @Test
// Read backwards public void testReadOutsideDataSeek0Read() throws IOException {
long newPos = stream.length() - result.length - i * result.length; byte[] data = new byte[256];
stream.seek(newPos); File file = randomDataToFile(data);
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
stream.readFully(result); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length)); assertEquals("File length should be same as stream length", file.length(), stream.length());
byte[] buffer = new byte[data.length * 2];
stream.read(buffer);
stream.seek(0);
assertNotEquals(-1, stream.read());
assertNotEquals(-1, stream.read(buffer));
} }
} }
@ -176,10 +193,10 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file); try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
for (int i = 1; i <= 64; i++) { assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit()); }
} }
} }
@ -190,12 +207,12 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file); try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
for (int i = 1; i <= 64; i++) { stream.seek(0);
stream.seek(0); assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i)); assertEquals(i % 8, stream.getBitOffset());
assertEquals(i % 8, stream.getBitOffset()); }
} }
} }
@ -206,13 +223,13 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file); try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 60; i++) {
for (int i = 1; i <= 60; i++) { stream.seek(0);
stream.seek(0); stream.setBitOffset(i % 8);
stream.setBitOffset(i % 8); assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i)); assertEquals(i * 2 % 8, stream.getBitOffset());
assertEquals(i * 2 % 8, stream.getBitOffset()); }
} }
} }
@ -221,35 +238,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
final ImageInputStream stream = new BufferedFileImageInputStream(file);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 2; i++) { try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals(buffer.getShort(), stream.readShort()); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
}
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 2; i++) {
@Override assertEquals(buffer.getShort(), stream.readShort());
public void run() throws Throwable {
stream.readShort();
} }
});
stream.seek(0); assertThrows(EOFException.class, new ThrowingRunnable() {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); @Override
buffer.position(0); public void run() throws Throwable {
buffer.order(ByteOrder.LITTLE_ENDIAN); stream.readShort();
}
});
for (int i = 0; i < bytes.length / 2; i++) { stream.seek(0);
assertEquals(buffer.getShort(), stream.readShort()); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
} buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 2; i++) {
@Override assertEquals(buffer.getShort(), stream.readShort());
public void run() throws Throwable {
stream.readShort();
} }
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
}
} }
@Test @Test
@ -257,35 +276,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
final ImageInputStream stream = new BufferedFileImageInputStream(file);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 4; i++) { try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals(buffer.getInt(), stream.readInt()); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
}
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 4; i++) {
@Override assertEquals(buffer.getInt(), stream.readInt());
public void run() throws Throwable {
stream.readInt();
} }
});
stream.seek(0); assertThrows(EOFException.class, new ThrowingRunnable() {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); @Override
buffer.position(0); public void run() throws Throwable {
buffer.order(ByteOrder.LITTLE_ENDIAN); stream.readInt();
}
});
for (int i = 0; i < bytes.length / 4; i++) { stream.seek(0);
assertEquals(buffer.getInt(), stream.readInt()); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
} buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 4; i++) {
@Override assertEquals(buffer.getInt(), stream.readInt());
public void run() throws Throwable {
stream.readInt();
} }
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
}
} }
@Test @Test
@ -293,35 +314,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
final ImageInputStream stream = new BufferedFileImageInputStream(file);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 8; i++) { try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals(buffer.getLong(), stream.readLong()); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
}
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 8; i++) {
@Override assertEquals(buffer.getLong(), stream.readLong());
public void run() throws Throwable {
stream.readLong();
} }
});
stream.seek(0); assertThrows(EOFException.class, new ThrowingRunnable() {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); @Override
buffer.position(0); public void run() throws Throwable {
buffer.order(ByteOrder.LITTLE_ENDIAN); stream.readLong();
}
});
for (int i = 0; i < bytes.length / 8; i++) { stream.seek(0);
assertEquals(buffer.getLong(), stream.readLong()); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
} buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 8; i++) {
@Override assertEquals(buffer.getLong(), stream.readLong());
public void run() throws Throwable {
stream.readLong();
} }
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
}
} }
@Test @Test
@ -329,49 +352,50 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[9]; byte[] bytes = new byte[9];
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
final ImageInputStream stream = new BufferedFileImageInputStream(file); try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.seek(1000); stream.seek(1000);
assertEquals(-1, stream.read()); assertEquals(-1, stream.read());
assertEquals(-1, stream.read(new byte[1], 0, 1)); assertEquals(-1, stream.read(new byte[1], 0, 1));
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readFully(new byte[1]); stream.readFully(new byte[1]);
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readByte(); stream.readByte();
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readShort(); stream.readShort();
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readInt(); stream.readInt();
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readLong(); stream.readLong();
} }
}); });
stream.seek(0); stream.seek(0);
for (byte value : bytes) { for (byte value : bytes) {
assertEquals(value, stream.readByte()); assertEquals(value, stream.readByte());
}
assertEquals(-1, stream.read());
} }
assertEquals(-1, stream.read());
} }
@Test @Test