--- old/src/share/classes/java/nio/file/Files.java 2013-07-25 03:37:29.802392021 +0400 +++ new/src/share/classes/java/nio/file/Files.java 2013-07-25 03:37:29.454396396 +0400 @@ -25,10 +25,10 @@ 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; @@ -2965,7 +2965,75 @@ } /** - * 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 + int newCapacity = capacity << 1; + if (newCapacity < BUFFER_SIZE) { + if (newCapacity >= 0) { + newCapacity = BUFFER_SIZE; + } else { // overflow + if (capacity == Integer.MAX_VALUE) + throw new OutOfMemoryError("Required array size too large"); + + newCapacity = (capacity < MAX_BUFFER_SIZE) ? + MAX_BUFFER_SIZE : + Integer.MAX_VALUE; + } + } + rem = newCapacity - capacity - 1; + buf = Arrays.copyOf(buf, newCapacity); + buf[nread++] = (byte)n; + capacity = newCapacity; + } + 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. * @@ -2989,27 +3057,18 @@ * 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. @@ -3469,7 +3528,7 @@ } /** - * 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. --- /dev/null 2013-07-24 14:02:09.040600594 +0400 +++ new/test/java/nio/file/Files/ReadAllBytesProc.java 2013-07-25 03:37:30.638381511 +0400 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013, 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 8020669 + * @summary j.n.f.Files.readAllBytes() should read from special files like + * /proc/self/stat under Linux eventhough OS reports them to be zero sized + */ + +import java.io.IOException; +import java.nio.file.*; + +public class ReadAllBytesProc { + + private static final String specFile = "/proc/self/stat"; + + public static void main(String[] args) { + try { + Path path = Paths.get(specFile); + byte[] data = Files.readAllBytes(path); + if (data.length == 0) + throw new RuntimeException("Failed to read content of '" + specFile + "'"); + } catch (IOException ex) { + System.err.println("Cannot perform test on this platform"); + } + } +}