#606: Workaround for broken JDK WBMPImageReader

(cherry picked from commit d0c4a075568304294b674de3f1d13a927e0084d0)
This commit is contained in:
Harald Kuhr 2021-04-29 16:55:24 +02:00
parent 34852f7be5
commit 4d45ea4966
2 changed files with 40 additions and 13 deletions

View File

@ -125,27 +125,35 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
} }
@Override @Override
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException { public int read(final byte[] bytes, final int offset, final int length) throws IOException {
checkClosed(); checkClosed();
bitOffset = 0; bitOffset = 0;
if (bufferEmpty()) { if (bufferEmpty()) {
// Bypass buffer if buffer is empty for reads longer than buffer // Bypass buffer if buffer is empty for reads longer than buffer
if (pLength >= buffer.length) { if (length >= buffer.length) {
return readDirect(pBuffer, pOffset, pLength); return readDirect(bytes, offset, length);
} }
else if (!fillBuffer()) { else if (!fillBuffer()) {
return -1; return -1;
} }
} }
return readBuffered(pBuffer, pOffset, pLength); int fromBuffer = readBuffered(bytes, offset, length);
if (length > fromBuffer) {
// Due to known bugs in certain JDK-bundled ImageIO plugins expecting read to behave as readFully,
// we'll read as much as possible from the buffer, and the rest directly after
return fromBuffer + max(0, readDirect(bytes, offset + fromBuffer, length - fromBuffer));
}
return fromBuffer;
} }
private int readDirect(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException { private int readDirect(final byte[] bytes, final int offset, final int length) throws IOException {
// Invalidate the buffer, as its contents is no longer in sync with the stream's position. // Invalidate the buffer, as its contents is no longer in sync with the stream's position.
bufferLimit = 0; bufferLimit = 0;
int read = raf.read(pBuffer, pOffset, pLength); int read = raf.read(bytes, offset, length);
if (read > 0) { if (read > 0) {
streamPos += read; streamPos += read;
@ -154,17 +162,17 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
return read; return read;
} }
private int readBuffered(final byte[] pBuffer, final int pOffset, final int pLength) { private int readBuffered(final byte[] bytes, final int offset, final int length) {
// Read as much as possible from buffer // Read as much as possible from buffer
int length = Math.min(bufferLimit - bufferPos, pLength); int available = Math.min(bufferLimit - bufferPos, length);
if (length > 0) { if (available > 0) {
System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length); System.arraycopy(buffer, bufferPos, bytes, offset, available);
bufferPos += length; bufferPos += available;
streamPos += length; streamPos += available;
} }
return length; return available;
} }
public long length() { public long length() {

View File

@ -383,4 +383,23 @@ public class BufferedFileImageInputStreamTest {
stream.close(); stream.close();
verify(mock, only()).close(); verify(mock, only()).close();
} }
@Test
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
// See #606 for details.
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
// Ie: Relies on read to return all bytes at once, without blocking
int size = BufferedFileImageInputStream.DEFAULT_BUFFER_SIZE * 7;
byte[] bytes = new byte[size];
File file = randomDataToFile(bytes);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
byte[] result = new byte[size];
int head = stream.read(result, 0, 12); // Provoke a buffered read
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
assertEquals(size, len + head);
assertArrayEquals(bytes, result);
}
}
} }