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

Print this page

        

@@ -26,10 +26,12 @@
 package java.nio.file;
 
 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.InputStream;
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.Writer;

@@ -2885,45 +2887,67 @@
             return copy(in, out);
         }
     }
 
     /**
-     * Read all the bytes from an input stream. The {@code initialSize}
-     * parameter indicates the initial size of the byte[] to allocate.
+     * 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) {
+            while ((n = source.read(buf, nread, capacity - nread)) > 0)
             nread += n;
-            rem -= n;
-            assert rem >= 0;
-            if (rem == 0) {
-                // need larger buffer
-                int newCapacity = capacity << 1;
-                if (newCapacity < 0) {
-                    if (capacity == Integer.MAX_VALUE)
+
+            // 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 larger buffer
+            if (capacity <= MAX_BUFFER_SIZE - capacity) {
+                capacity = Math.max(capacity << 1, BUFFER_SIZE);
+            } else {
+                if (capacity == MAX_BUFFER_SIZE)
                         throw new OutOfMemoryError("Required array size too large");
-                    newCapacity = Integer.MAX_VALUE;
-                }
-                rem = newCapacity - capacity;
-                buf = Arrays.copyOf(buf, newCapacity);
-                capacity = newCapacity;
+                capacity = MAX_BUFFER_SIZE;
             }
+            buf = Arrays.copyOf(buf, capacity);
+            buf[nread++] = (byte)n;
         }
         return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
     }
 
     /**
-     * Read all the bytes from a file. The method ensures that the file is
+     * 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

@@ -2943,16 +2967,17 @@
      *          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 {
-        long size = size(path);
-        if (size > (long)Integer.MAX_VALUE)
+        try (FileChannel fc = FileChannel.open(path);
+             InputStream is = Channels.newInputStream(fc)) {
+            long size = fc.size();
+            if (size > (long)MAX_BUFFER_SIZE)
             throw new OutOfMemoryError("Required array size too large");
 
-        try (InputStream in = newInputStream(path)) {
-             return read(in, (int)size);
+            return read(is, (int)size);
         }
     }
 
     /**
      * Read all lines from a file. This method ensures that the file is