8227080: (fs) Files.newInputStream(...).skip(n) is slow
Reviewed-by: sbordet, rriggs, fweimer
--- a/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java Wed Jul 03 14:18:15 2019 -0700
+++ b/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java Wed Jul 03 14:37:19 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.*;
-
+import java.util.Objects;
/**
* This class is defined here rather than in java.nio.channels.Channels
@@ -87,10 +87,8 @@
public synchronized int read(byte[] bs, int off, int len)
throws IOException
{
- if ((off < 0) || (off > bs.length) || (len < 0) ||
- ((off + len) > bs.length) || ((off + len) < 0)) {
- throw new IndexOutOfBoundsException();
- } else if (len == 0)
+ Objects.checkFromIndexSize(off, len, bs.length);
+ if (len == 0)
return 0;
ByteBuffer bb = ((this.bs == bs)
@@ -119,6 +117,26 @@
return 0;
}
+ public synchronized long skip(long n) throws IOException {
+ // special case where the channel is to a file
+ if (ch instanceof SeekableByteChannel && n > 0) {
+ SeekableByteChannel sbc = (SeekableByteChannel)ch;
+ try {
+ long pos = sbc.position();
+ long size = sbc.size();
+ if (pos >= size) {
+ return 0L;
+ }
+ n = Math.min(n, size - pos);
+ sbc.position(pos + n);
+ return sbc.position() - pos;
+ } catch (ClosedChannelException cce) {
+ throw new IOException(cce);
+ }
+ }
+ return super.skip(n);
+ }
+
public void close() throws IOException {
ch.close();
}
--- a/test/jdk/java/nio/file/Files/Misc.java Wed Jul 03 14:18:15 2019 -0700
+++ b/test/jdk/java/nio/file/Files/Misc.java Wed Jul 03 14:37:19 2019 -0700
@@ -22,11 +22,14 @@
*/
/* @test
- * @bug 4313887 6838333 8005566 8032220 8215467
+ * @bug 4313887 6838333 8005566 8032220 8215467 8227080
* @summary Unit test for miscellenous methods in java.nio.file.Files
* @library ..
*/
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.ClosedChannelException;
import java.nio.file.*;
import static java.nio.file.Files.*;
import static java.nio.file.LinkOption.*;
@@ -44,6 +47,7 @@
testIsSameFile(dir);
testFileTypeMethods(dir);
testAccessMethods(dir);
+ testSkip(dir);
} finally {
TestUtil.removeAll(dir);
}
@@ -372,6 +376,38 @@
}
}
+ /**
+ * Tests Files.newInputStream(Path).skip().
+ */
+ static void testSkip(Path tmpdir) throws IOException {
+ Path file = createFile(tmpdir.resolve("foo"));
+ try (OutputStream out = Files.newOutputStream(file)) {
+ byte[] blah = new byte[8192];
+ Arrays.fill(blah, (byte)42);
+ out.write(blah);
+ out.close();
+ try (InputStream in = Files.newInputStream(file)) {
+ assertTrue(in.skip(-1) == 0);
+ assertTrue(in.skip(0) == 0);
+ assertTrue(in.skip(blah.length/4) == blah.length/4);
+ assertTrue(in.skip(blah.length/2) == blah.length/2);
+ assertTrue(in.skip(Long.MAX_VALUE) == blah.length/4);
+ in.close();
+ try {
+ long n = in.skip(1);
+ throw new RuntimeException("skip() did not fail");
+ } catch (IOException ioe) {
+ if (!(ioe.getCause() instanceof ClosedChannelException)) {
+ throw new RuntimeException
+ ("IOException not caused by ClosedChannelException");
+ }
+ }
+ }
+ } finally {
+ delete(file);
+ }
+ }
+
static void assertTrue(boolean okay) {
if (!okay)
throw new RuntimeException("Assertion Failed");