diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/MemoryCache.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/MemoryCache.java index 9e8bbae6..ad24ea5c 100644 --- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/MemoryCache.java +++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/MemoryCache.java @@ -17,8 +17,12 @@ final class MemoryCache implements Cache { final static int BLOCK_SIZE = 1 << 13; + private static final byte[] NULL_BLOCK = new byte[0]; + private final List cache = new ArrayList<>(); private final ReadableByteChannel channel; + + private int maxBlock = Integer.MAX_VALUE; private long length; private long position; private long start; @@ -34,12 +38,14 @@ final class MemoryCache implements Cache { byte[] fetchBlock() throws IOException { long currPos = position; - long index = currPos / BLOCK_SIZE; if (index >= Integer.MAX_VALUE) { throw new IOException("Memory cache max size exceeded"); } + if (index > maxBlock) { + return NULL_BLOCK; + } while (index >= cache.size()) { byte[] block; @@ -51,7 +57,14 @@ final class MemoryCache implements Cache { } cache.add(block); - length += readBlock(block); + int bytesRead = readBlock(block); + length += bytesRead; + + if (bytesRead < BLOCK_SIZE) { + // Last block, EOF found + maxBlock = (int) index; + return block; + } } return cache.get((int) index); @@ -63,7 +76,7 @@ final class MemoryCache implements Cache { while (wrapped.hasRemaining()) { int count = channel.read(wrapped); if (count == -1) { - // Last block + // Last block, EOF found break; } } @@ -84,12 +97,12 @@ final class MemoryCache implements Cache { @Override public int read(ByteBuffer dest) throws IOException { byte[] buffer = fetchBlock(); - int bufferPos = (int) (position % BLOCK_SIZE); if (position >= length) { return -1; } + int bufferPos = (int) (position % BLOCK_SIZE); int len = min(dest.remaining(), (int) min(BLOCK_SIZE - bufferPos, length - position)); dest.put(buffer, bufferPos, len); diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedChannelImageInputStreamMemoryCacheTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedChannelImageInputStreamMemoryCacheTest.java index 588aa729..966394a4 100755 --- a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedChannelImageInputStreamMemoryCacheTest.java +++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedChannelImageInputStreamMemoryCacheTest.java @@ -402,6 +402,24 @@ public class BufferedChannelImageInputStreamMemoryCacheTest { assertEquals(-1, stream.read()); } } + @Test + public void testSeekWayPastEOFShouldNotThrowOOME() throws IOException { + byte[] bytes = new byte[9]; + InputStream input = randomDataToInputStream(bytes); + + try (final ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(input))) { + stream.seek(Integer.MAX_VALUE * 4L * 512L); // ~4 TB + + assertEquals(-1, stream.read()); // No OOME should happen... + + stream.seek(0); + for (byte value : bytes) { + assertEquals(value, stream.readByte()); + } + + assertEquals(-1, stream.read()); + } + } @Test public void testClose() throws IOException {