8227609: (fs) Files.newInputStream(...).skip(n) should allow skipping beyond file size
Reviewed-by: alanb, lancea, fweimer
--- a/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java Tue Jul 30 17:54:53 2019 +0200
+++ b/src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java Tue Jul 30 09:46:06 2019 -0700
@@ -119,20 +119,21 @@
public synchronized long skip(long n) throws IOException {
// special case where the channel is to a file
- if (ch instanceof SeekableByteChannel && n > 0) {
+ if (ch instanceof SeekableByteChannel) {
SeekableByteChannel sbc = (SeekableByteChannel)ch;
- try {
- long pos = sbc.position();
+ long pos = sbc.position();
+ long newPos;
+ if (n > 0) {
+ newPos = pos + n;
long size = sbc.size();
- if (pos >= size) {
- return 0L;
+ if (newPos < 0 || newPos > size) {
+ newPos = size;
}
- n = Math.min(n, size - pos);
- sbc.position(pos + n);
- return sbc.position() - pos;
- } catch (ClosedChannelException cce) {
- throw new IOException(cce);
+ } else {
+ newPos = Long.max(pos + n, 0);
}
+ sbc.position(newPos);
+ return newPos - pos;
}
return super.skip(n);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/file/Files/InputStreamTest.java Tue Jul 30 09:46:06 2019 -0700
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8227609
+ * @summary Test of InputStream and OutputStream created by 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.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+
+public class InputStreamTest {
+
+ public static void main(String[] args) throws IOException {
+ Path dir = TestUtil.createTemporaryDirectory();
+ try {
+ testSkip(dir);
+ } finally {
+ TestUtil.removeAll(dir);
+ }
+ }
+
+ /**
+ * Tests Files.newInputStream(Path).skip().
+ */
+ static void testSkip(Path tmpdir) throws IOException {
+ Path file = createFile(tmpdir.resolve("foo"));
+ try (OutputStream out = Files.newOutputStream(file)) {
+ final int size = 512;
+ byte[] blah = new byte[size];
+ for (int i = 0; i < size; i++) {
+ blah[i] = (byte)(i % 128);
+ }
+ out.write(blah);
+ out.close();
+
+ try (InputStream in = Files.newInputStream(file)) {
+ assertTrue(in.available() == size);
+ assertTrue(in.skip(size/4) == size/4); // 0.25
+ assertTrue(in.available() == 3*size/4);
+
+ int b = in.read();
+ assertTrue(b == blah[size/4]);
+ assertTrue(in.available() == 3*size/4 - 1);
+ assertTrue(in.skip(-1) == -1); // 0.25
+ assertTrue(in.available() == 3*size/4);
+
+ assertTrue(in.skip(-size/2) == -size/4); // 0
+ assertTrue(in.available() == size);
+
+ assertTrue(in.skip(5*size/4) == size); // 1.0
+ assertTrue(in.available() == 0);
+
+ assertTrue(in.skip(-3*size/4) == -3*size/4); // 0.25
+ assertTrue(in.available() == 3*size/4);
+
+ byte[] buf = new byte[16];
+ in.read(buf, 2, 12);
+ assertTrue(Arrays.equals(buf, 2, 14,
+ blah, size/4, size/4 + 12));
+ assertTrue(in.skip(-12) == -12); // 0.25
+
+ assertTrue(in.skip(3*size/4) == 3*size/4); // 1.0
+ assertTrue(in.available() == 0);
+
+ assertTrue(in.skip(-size/2) == -size/2); // 0.5
+ assertTrue(in.available() == size/2);
+
+ assertTrue(in.skip(-size) == -size/2); // 0
+ assertTrue(in.available() == size);
+
+ assertTrue(in.skip(size/2) == size/2); // 0.5
+ assertTrue(in.available() == size/2);
+
+ assertTrue(in.skip(Long.MIN_VALUE) == -size/2); // 0
+ assertTrue(in.available() == size);
+
+ assertTrue(in.skip(size/2) == size/2); // 0.5
+ assertTrue(in.available() == size/2);
+
+ assertTrue(in.skip(Long.MAX_VALUE - size/4) == size/2);
+ assertTrue(in.available() == 0);
+
+ in.close();
+ try {
+ in.skip(1);
+ throw new RuntimeException("skip() did not fail");
+ } catch (IOException ioe) {
+ if (!(ioe instanceof ClosedChannelException)) {
+ throw new RuntimeException
+ ("IOException is not a ClosedChannelException");
+ }
+ }
+ }
+ }
+ }
+
+ static void assertTrue(boolean okay) {
+ if (!okay)
+ throw new RuntimeException("Assertion Failed");
+ }
+}
--- a/test/jdk/java/nio/file/Files/Misc.java Tue Jul 30 17:54:53 2019 +0200
+++ b/test/jdk/java/nio/file/Files/Misc.java Tue Jul 30 09:46:06 2019 -0700
@@ -22,14 +22,11 @@
*/
/* @test
- * @bug 4313887 6838333 8005566 8032220 8215467 8227080
+ * @bug 4313887 6838333 8005566 8032220 8215467
* @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.*;
@@ -47,7 +44,6 @@
testIsSameFile(dir);
testFileTypeMethods(dir);
testAccessMethods(dir);
- testSkip(dir);
} finally {
TestUtil.removeAll(dir);
}
@@ -376,38 +372,6 @@
}
}
- /**
- * 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");