diff options
Diffstat (limited to 'src/share/classes/sun/misc/IOUtils.java')
-rw-r--r-- | src/share/classes/sun/misc/IOUtils.java | 291 |
1 files changed, 253 insertions, 38 deletions
diff --git a/src/share/classes/sun/misc/IOUtils.java b/src/share/classes/sun/misc/IOUtils.java index 67079b9a67..5e6c79cb64 100644 --- a/src/share/classes/sun/misc/IOUtils.java +++ b/src/share/classes/sun/misc/IOUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -32,67 +32,282 @@ package sun.misc; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; + +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Objects; public class IOUtils { + private static final int DEFAULT_BUFFER_SIZE = 8192; + /** - * Read up to {@code length} of bytes from {@code in} - * until EOF is detected. + * 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; + + /** + * Read exactly {@code length} of bytes from {@code in}. + * + * <p> Note that this method is safe to be called with unknown large + * {@code length} argument. The memory used is proportional to the + * actual bytes available. An exception is thrown if there are not + * enough bytes in the stream. + * * @param is input stream, must not be null - * @param length number of bytes to read, -1 or Integer.MAX_VALUE means - * read as much as possible - * @param readAll if true, an EOFException will be thrown if not enough - * bytes are read. Ignored when length is -1 or Integer.MAX_VALUE + * @param length number of bytes to read * @return bytes read - * @throws IOException Any IO error or a premature EOF is detected + * @throws EOFException if there are not enough bytes in the stream + * @throws IOException if an I/O error occurs or {@code length} is negative + * @throws OutOfMemoryError if an array of the required size cannot be + * allocated. */ - public static byte[] readFully(InputStream is, int length, boolean readAll) + public static byte[] readExactlyNBytes(InputStream is, int length) throws IOException { - byte[] output = {}; - if (length == -1) length = Integer.MAX_VALUE; - int pos = 0; - while (pos < length) { - int bytesToRead; - if (pos >= output.length) { // Only expand when there's no room - bytesToRead = Math.min(length - pos, output.length + 1024); - if (output.length < pos + bytesToRead) { - output = Arrays.copyOf(output, pos + bytesToRead); - } - } else { - bytesToRead = output.length - pos; + if (length < 0) { + throw new IOException("length cannot be negative: " + length); + } + byte[] data = readNBytes(is, length); + if (data.length < length) { + throw new EOFException(); + } + return data; + } + + /** + * Reads all remaining bytes from the input stream. This method blocks until + * all remaining bytes have been read and end of stream is detected, or an + * exception is thrown. This method does not close the input stream. + * + * <p> When this stream reaches end of stream, further invocations of this + * method will return an empty byte array. + * + * <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 + * reading input streams with large amounts of data. + * + * <p> The behavior for the case where the input stream is <i>asynchronously + * closed</i>, or the thread interrupted during the read, is highly input + * stream specific, and therefore not specified. + * + * <p> If an I/O error occurs reading from the input stream, then it may do + * so after some, but not all, bytes have been read. Consequently the input + * stream may not be at end of stream and may be in an inconsistent state. + * It is strongly recommended that the stream be promptly closed if an I/O + * error occurs. + * + * @implSpec + * This method invokes {@link #readNBytes(int)} with a length of + * {@link Integer#MAX_VALUE}. + * + * @param is input stream, must not be null + * @return a byte array containing the bytes read from this input stream + * @throws IOException if an I/O error occurs + * @throws OutOfMemoryError if an array of the required size cannot be + * allocated. + * + * @since 1.9 + */ + public static byte[] readAllBytes(InputStream is) throws IOException { + return readNBytes(is, Integer.MAX_VALUE); + } + + /** + * Reads up to a specified number of bytes from the input stream. This + * method blocks until the requested number of bytes have been read, end + * of stream is detected, or an exception is thrown. This method does not + * close the input stream. + * + * <p> The length of the returned array equals the number of bytes read + * from the stream. If {@code len} is zero, then no bytes are read and + * an empty byte array is returned. Otherwise, up to {@code len} bytes + * are read from the stream. Fewer than {@code len} bytes may be read if + * end of stream is encountered. + * + * <p> When this stream reaches end of stream, further invocations of this + * method will return an empty byte array. + * + * <p> Note that this method is intended for simple cases where it is + * convenient to read the specified number of bytes into a byte array. The + * total amount of memory allocated by this method is proportional to the + * number of bytes read from the stream which is bounded by {@code len}. + * Therefore, the method may be safely called with very large values of + * {@code len} provided sufficient memory is available. + * + * <p> The behavior for the case where the input stream is <i>asynchronously + * closed</i>, or the thread interrupted during the read, is highly input + * stream specific, and therefore not specified. + * + * <p> If an I/O error occurs reading from the input stream, then it may do + * so after some, but not all, bytes have been read. Consequently the input + * stream may not be at end of stream and may be in an inconsistent state. + * It is strongly recommended that the stream be promptly closed if an I/O + * error occurs. + * + * @implNote + * The number of bytes allocated to read data from this stream and return + * the result is bounded by {@code 2*(long)len}, inclusive. + * + * @param is input stream, must not be null + * @param len the maximum number of bytes to read + * @return a byte array containing the bytes read from this input stream + * @throws IllegalArgumentException if {@code length} is negative + * @throws IOException if an I/O error occurs + * @throws OutOfMemoryError if an array of the required size cannot be + * allocated. + * + * @since 11 + */ + public static byte[] readNBytes(InputStream is, int len) throws IOException { + if (len < 0) { + throw new IllegalArgumentException("len < 0"); + } + + List<byte[]> bufs = null; + byte[] result = null; + int total = 0; + int remaining = len; + int n; + do { + byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)]; + int nread = 0; + + // read to EOF which may read more or less than buffer size + while ((n = is.read(buf, nread, + Math.min(buf.length - nread, remaining))) > 0) { + nread += n; + remaining -= n; } - int cc = is.read(output, pos, bytesToRead); - if (cc < 0) { - if (readAll && length != Integer.MAX_VALUE) { - throw new EOFException("Detect premature EOF"); + + if (nread > 0) { + if (MAX_BUFFER_SIZE - total < nread) { + throw new OutOfMemoryError("Required array size too large"); + } + total += nread; + if (result == null) { + result = buf; } else { - if (output.length != pos) { - output = Arrays.copyOf(output, pos); + if (bufs == null) { + bufs = new ArrayList<>(); + bufs.add(result); } - break; + bufs.add(buf); } } - pos += cc; + // if the last call to read returned -1 or the number of bytes + // requested have been read then break + } while (n >= 0 && remaining > 0); + + if (bufs == null) { + if (result == null) { + return new byte[0]; + } + return result.length == total ? + result : Arrays.copyOf(result, total); + } + + result = new byte[total]; + int offset = 0; + remaining = total; + for (byte[] b : bufs) { + int count = Math.min(b.length, remaining); + System.arraycopy(b, 0, result, offset, count); + offset += count; + remaining -= count; + } + + return result; + } + + /** + * Reads the requested number of bytes from the input stream into the given + * byte array. This method blocks until {@code len} bytes of input data have + * been read, end of stream is detected, or an exception is thrown. The + * number of bytes actually read, possibly zero, is returned. This method + * does not close the input stream. + * + * <p> In the case where end of stream is reached before {@code len} bytes + * have been read, then the actual number of bytes read will be returned. + * When this stream reaches end of stream, further invocations of this + * method will return zero. + * + * <p> If {@code len} is zero, then no bytes are read and {@code 0} is + * returned; otherwise, there is an attempt to read up to {@code len} bytes. + * + * <p> The first byte read is stored into element {@code b[off]}, the next + * one in to {@code b[off+1]}, and so on. The number of bytes read is, at + * most, equal to {@code len}. Let <i>k</i> be the number of bytes actually + * read; these bytes will be stored in elements {@code b[off]} through + * {@code b[off+}<i>k</i>{@code -1]}, leaving elements {@code b[off+}<i>k</i> + * {@code ]} through {@code b[off+len-1]} unaffected. + * + * <p> The behavior for the case where the input stream is <i>asynchronously + * closed</i>, or the thread interrupted during the read, is highly input + * stream specific, and therefore not specified. + * + * <p> If an I/O error occurs reading from the input stream, then it may do + * so after some, but not all, bytes of {@code b} have been updated with + * data from the input stream. Consequently the input stream and {@code b} + * may be in an inconsistent state. It is strongly recommended that the + * stream be promptly closed if an I/O error occurs. + * + * @param is input stream, must not be null + * @param b the byte array into which the data is read + * @param off the start offset in {@code b} at which the data is written + * @param len the maximum number of bytes to read + * @return the actual number of bytes read into the buffer + * @throws IOException if an I/O error occurs + * @throws NullPointerException if {@code b} is {@code null} + * @throws IndexOutOfBoundsException If {@code off} is negative, {@code len} + * is negative, or {@code len} is greater than {@code b.length - off} + * + * @since 1.9 + */ + public static int readNBytes(InputStream is, byte[] b, int off, int len) throws IOException { + Objects.requireNonNull(b); + if (off < 0 || len < 0 || len > b.length - off) + throw new IndexOutOfBoundsException(); + int n = 0; + while (n < len) { + int count = is.read(b, off + n, len - n); + if (count < 0) + break; + n += count; } - return output; + return n; } /** - * Read {@code length} of bytes from {@code in}. An exception is - * thrown if there are not enough bytes in the stream. + * Compatibility wrapper for third party users of + * {@code sun.misc.IOUtils.readFully} following its + * removal in JDK-8231139. + * + * Read up to {@code length} of bytes from {@code in} + * until EOF is detected. * * @param is input stream, must not be null - * @param length number of bytes to read, must not be negative + * @param length number of bytes to read + * @param readAll if true, an EOFException will be thrown if not enough + * bytes are read. * @return bytes read - * @throws IOException if any IO error or a premature EOF is detected, or - * if {@code length} is negative since this length is usually also - * read from {@code is}. + * @throws EOFException if there are not enough bytes in the stream + * @throws IOException if an I/O error occurs or {@code length} is negative + * @throws OutOfMemoryError if an array of the required size cannot be + * allocated. */ - public static byte[] readNBytes(InputStream is, int length) throws IOException { + public static byte[] readFully(InputStream is, int length, boolean readAll) + throws IOException { if (length < 0) { throw new IOException("length cannot be negative: " + length); } - return readFully(is, length, true); + if (readAll) { + return readExactlyNBytes(is, length); + } else { + return readNBytes(is, length); + } } } |