From 4d45ea496689de1a458df24ecfe8793f6c373f7a Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Thu, 29 Apr 2021 16:55:24 +0200 Subject: [PATCH] #606: Workaround for broken JDK WBMPImageReader (cherry picked from commit d0c4a075568304294b674de3f1d13a927e0084d0) --- .../stream/BufferedFileImageInputStream.java | 34 ++++++++++++------- .../BufferedFileImageInputStreamTest.java | 19 +++++++++++ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStream.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStream.java index 50106903..91dc36f8 100644 --- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStream.java +++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStream.java @@ -125,27 +125,35 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl { } @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(); bitOffset = 0; if (bufferEmpty()) { // Bypass buffer if buffer is empty for reads longer than buffer - if (pLength >= buffer.length) { - return readDirect(pBuffer, pOffset, pLength); + if (length >= buffer.length) { + return readDirect(bytes, offset, length); } else if (!fillBuffer()) { 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. bufferLimit = 0; - int read = raf.read(pBuffer, pOffset, pLength); + int read = raf.read(bytes, offset, length); if (read > 0) { streamPos += read; @@ -154,17 +162,17 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl { 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 - int length = Math.min(bufferLimit - bufferPos, pLength); + int available = Math.min(bufferLimit - bufferPos, length); - if (length > 0) { - System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length); - bufferPos += length; - streamPos += length; + if (available > 0) { + System.arraycopy(buffer, bufferPos, bytes, offset, available); + bufferPos += available; + streamPos += available; } - return length; + return available; } public long length() { diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamTest.java index 822db90e..e86c7320 100755 --- a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamTest.java +++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamTest.java @@ -383,4 +383,23 @@ public class BufferedFileImageInputStreamTest { stream.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); + } + } }