src/share/classes/java/nio/file/Files.java

Print this page

        

@@ -23,14 +23,14 @@
  * questions.
  */
 
 package java.nio.file;
 
-import java.nio.ByteBuffer;
 import java.nio.file.attribute.*;
 import java.nio.file.spi.FileSystemProvider;
 import java.nio.file.spi.FileTypeDetector;
+import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 import java.nio.channels.SeekableByteChannel;
 import java.io.Closeable;
 import java.io.InputStream;
 import java.io.OutputStream;

@@ -2963,11 +2963,74 @@
             return copy(in, out);
         }
     }
 
     /**
-     * Read all the bytes from a file. The method ensures that the file is
+     * The maximum size of array to allocate.
+     * Some VMs reserve some header words in an array.
+     * Attempts to allocate larger arrays may result in
+     * OutOfMemoryError: Requested array size exceeds VM limit
+     */
+    private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
+
+    /**
+     * Reads all the bytes from an input stream. Uses {@code initialSize} as a hint
+     * about how many bytes the stream will have.
+     *
+     * @param   source
+     *          the input stream to read from
+     * @param   initialSize
+     *          the initial size of the byte array to allocate
+     *
+     * @return  a byte array containing the bytes read from the file
+     *
+     * @throws  IOException
+     *          if an I/O error occurs reading from the stream
+     * @throws  OutOfMemoryError
+     *          if an array of the required size cannot be allocated
+     */
+    private static byte[] read(InputStream source, int initialSize)
+            throws IOException
+    {
+        int capacity = initialSize;
+        byte[] buf = new byte[capacity];
+        int nread = 0;
+        int rem = buf.length;
+        int n;
+        for (;;) {
+            // read to EOF which may read more or less than initialSize (eg: file
+            // is truncated while we are reading)
+            while ((n = source.read(buf, nread, rem)) > 0) {
+                nread += n;
+                rem -= n;
+                assert rem >= 0;
+            }
+
+            // if last call to source.read() returned -1, we are done
+            // otherwise, try to read one more byte; if that failed we're done too
+            if (n < 0 || (n = source.read()) < 0)
+                break;
+
+            // one more byte was read; need to allocate a bigger buffer
+            rem = Math.max(capacity, BUFFER_SIZE - capacity);
+            if (MAX_BUFFER_SIZE - rem < capacity) {
+                if (capacity == Integer.MAX_VALUE)
+                    throw new OutOfMemoryError("Required array size too large");
+                rem = (capacity < MAX_BUFFER_SIZE) ?
+                        MAX_BUFFER_SIZE - capacity :
+                        Integer.MAX_VALUE - capacity;
+            }
+            capacity += rem;
+            buf = Arrays.copyOf(buf, capacity);
+            buf[nread++] = (byte)n;
+            rem--;
+        }
+        return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
+    }
+
+    /**
+     * Reads all the bytes from a file. The method ensures that the file is
      * closed when all bytes have been read or an I/O error, or other runtime
      * exception, is thrown.
      *
      * <p> Note that this method is intended for simple cases where it is
      * convenient to read all bytes into a byte array. It is not intended for

@@ -2987,31 +3050,22 @@
      *          In the case of the default provider, and a security manager is
      *          installed, the {@link SecurityManager#checkRead(String) checkRead}
      *          method is invoked to check read access to the file.
      */
     public static byte[] readAllBytes(Path path) throws IOException {
-        try (FileChannel fc = FileChannel.open(path)) {
+        try (FileChannel fc = FileChannel.open(path);
+             InputStream fis = Channels.newInputStream(fc)) {
             long size = fc.size();
             if (size > (long)Integer.MAX_VALUE)
                 throw new OutOfMemoryError("Required array size too large");
 
-            byte[] arr = new byte[(int)size];
-            ByteBuffer bb = ByteBuffer.wrap(arr);
-            while (bb.hasRemaining()) {
-                if (fc.read(bb) < 0) {
-                    // truncated
-                    break;
-                }
-            }
-
-            int nread = bb.position();
-            return (nread == size) ? arr : Arrays.copyOf(arr, nread);
+            return read(fis, (int)size);
         }
     }
 
     /**
-     * Read all lines from a file. This method ensures that the file is
+     * Reads all lines from a file. This method ensures that the file is
      * closed when all bytes have been read or an I/O error, or other runtime
      * exception, is thrown. Bytes from the file are decoded into characters
      * using the specified charset.
      *
      * <p> This method recognizes the following as line terminators:

@@ -3467,11 +3521,11 @@
                 map(entry -> entry.file());
         return new DelegatingCloseableStream<>(iterator, s);
     }
 
     /**
-     * Read all lines from a file as a {@code CloseableStream}.  Unlike {@link
+     * Reads all lines from a file as a {@code CloseableStream}.  Unlike {@link
      * #readAllLines(Path, Charset) readAllLines}, this method does not read
      * all lines into a {@code List}, but instead populates lazily as the stream
      * is consumed.
      *
      * <p> Bytes from the file are decoded into characters using the specified