8227609: (fs) Files.newInputStream(...).skip(n) should allow skipping beyond file size
authorbpb
Tue, 30 Jul 2019 09:46:06 -0700
changeset 57595 d629735db937
parent 57594 61c2e3e7315d
child 57596 dad0062bb7f3
8227609: (fs) Files.newInputStream(...).skip(n) should allow skipping beyond file size Reviewed-by: alanb, lancea, fweimer
src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java
test/jdk/java/nio/file/Files/InputStreamTest.java
test/jdk/java/nio/file/Files/Misc.java
--- 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");