diff options
author | andrew <unknown> | 2020-01-03 00:47:11 +0000 |
---|---|---|
committer | bell-sw <liberica@bell-sw.com> | 2020-01-19 09:13:22 +0300 |
commit | 862b669c8bf78794b4392857123c6256b494a94e (patch) | |
tree | 2d30f3ae11e2d8476eccc84e5963b72afa2da091 | |
parent | 25d05e980ec33f83e4fe613ce007c6bac9c21b20 (diff) | |
download | jdk8u_jdk-862b669c8bf78794b4392857123c6256b494a94e.tar.gz |
8080835: Add blocking bulk read to sun.misc.IOUtils
Reviewed-by: mbalao
-rw-r--r-- | src/share/classes/sun/misc/IOUtils.java | 129 | ||||
-rw-r--r-- | test/sun/misc/IOUtils/ReadAllBytes.java | 107 | ||||
-rw-r--r-- | test/sun/misc/IOUtils/ReadNBytes.java | 118 |
3 files changed, 354 insertions, 0 deletions
diff --git a/src/share/classes/sun/misc/IOUtils.java b/src/share/classes/sun/misc/IOUtils.java index 67079b9a67..e857b6a795 100644 --- a/src/share/classes/sun/misc/IOUtils.java +++ b/src/share/classes/sun/misc/IOUtils.java @@ -32,10 +32,22 @@ package sun.misc; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; + import java.util.Arrays; +import java.util.Objects; public class IOUtils { + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * 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 up to {@code length} of bytes from {@code in} * until EOF is detected. @@ -95,4 +107,121 @@ public class IOUtils { } return readFully(is, length, true); } + + /** + * 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. + * + * @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. For example, if an array larger than {@code 2GB} would + * be required to store the bytes. + * + * @since 1.9 + */ + public static byte[] readAllBytes(InputStream is) throws IOException { + byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; + int capacity = buf.length; + int nread = 0; + int n; + for (;;) { + // read to EOF which may read more or less than initial buffer size + while ((n = is.read(buf, nread, capacity - nread)) > 0) + nread += n; + + // if the last call to read returned -1, then we're done + if (n < 0) + break; + + // need to allocate a larger buffer + if (capacity <= MAX_BUFFER_SIZE - capacity) { + capacity = capacity << 1; + } else { + if (capacity == MAX_BUFFER_SIZE) + throw new OutOfMemoryError("Required array size too large"); + capacity = MAX_BUFFER_SIZE; + } + buf = Arrays.copyOf(buf, capacity); + } + return (capacity == nread) ? buf : Arrays.copyOf(buf, nread); + } + + /** + * 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 n; + } + } diff --git a/test/sun/misc/IOUtils/ReadAllBytes.java b/test/sun/misc/IOUtils/ReadAllBytes.java new file mode 100644 index 0000000000..2d7a30b2b2 --- /dev/null +++ b/test/sun/misc/IOUtils/ReadAllBytes.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Random; + +import jdk.testlibrary.RandomFactory; + +import sun.misc.IOUtils; + +/* + * @test + * @bug 8080835 + * @library /lib/testlibrary + * @build jdk.testlibrary.* + * @run main ReadAllBytes + * @summary Basic test for IOUtils.readAllBytes + * @key randomness + */ + +public class ReadAllBytes { + + private static Random generator = RandomFactory.getRandom(); + + public static void main(String[] args) throws IOException { + test(new byte[]{}); + test(new byte[]{1, 2, 3}); + test(createRandomBytes(1024)); + test(createRandomBytes((1 << 13) - 1)); + test(createRandomBytes((1 << 13))); + test(createRandomBytes((1 << 13) + 1)); + test(createRandomBytes((1 << 15) - 1)); + test(createRandomBytes((1 << 15))); + test(createRandomBytes((1 << 15) + 1)); + test(createRandomBytes((1 << 17) - 1)); + test(createRandomBytes((1 << 17))); + test(createRandomBytes((1 << 17) + 1)); + } + + static void test(byte[] expectedBytes) throws IOException { + int expectedLength = expectedBytes.length; + WrapperInputStream in = new WrapperInputStream(new ByteArrayInputStream(expectedBytes)); + byte[] readBytes = IOUtils.readAllBytes(in); + + int x; + byte[] tmp = new byte[10]; + check((x = in.read()) == -1, + "Expected end of stream from read(), got " + x); + check((x = in.read(tmp)) == -1, + "Expected end of stream from read(byte[]), got " + x); + check((x = in.read(tmp, 0, tmp.length)) == -1, + "Expected end of stream from read(byte[], int, int), got " + x); + check(IOUtils.readAllBytes(in).length == 0, + "Expected readAllBytes to return empty byte array"); + check(expectedLength == readBytes.length, + "Expected length " + expectedLength + ", got " + readBytes.length); + check(Arrays.equals(expectedBytes, readBytes), + "Expected[" + expectedBytes + "], got:[" + readBytes + "]"); + check(!in.isClosed(), "Stream unexpectedly closed"); + } + + static byte[] createRandomBytes(int size) { + byte[] bytes = new byte[size]; + generator.nextBytes(bytes); + return bytes; + } + + static void check(boolean cond, Object ... failedArgs) { + if (cond) + return; + StringBuilder sb = new StringBuilder(); + for (Object o : failedArgs) + sb.append(o); + throw new RuntimeException(sb.toString()); + } + + static class WrapperInputStream extends FilterInputStream { + private boolean closed; + WrapperInputStream(InputStream in) { super(in); } + @Override public void close() throws IOException { closed = true; in.close(); } + boolean isClosed() { return closed; } + } +} diff --git a/test/sun/misc/IOUtils/ReadNBytes.java b/test/sun/misc/IOUtils/ReadNBytes.java new file mode 100644 index 0000000000..afe943fefb --- /dev/null +++ b/test/sun/misc/IOUtils/ReadNBytes.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Random; +import jdk.testlibrary.RandomFactory; + +import sun.misc.IOUtils; + +/* + * @test + * @bug 8080835 + * @library /lib/testlibrary + * @build jdk.testlibrary.* + * @run main ReadNBytes + * @summary Basic test for IOUtils.readNBytes + * @key randomness + */ + +public class ReadNBytes { + + private static Random generator = RandomFactory.getRandom(); + + public static void main(String[] args) throws IOException { + test(new byte[]{1, 2, 3}); + test(createRandomBytes(1024)); + test(createRandomBytes((1 << 13) - 1)); + test(createRandomBytes((1 << 13))); + test(createRandomBytes((1 << 13) + 1)); + test(createRandomBytes((1 << 15) - 1)); + test(createRandomBytes((1 << 15))); + test(createRandomBytes((1 << 15) + 1)); + test(createRandomBytes((1 << 17) - 1)); + test(createRandomBytes((1 << 17))); + test(createRandomBytes((1 << 17) + 1)); + } + + static void test(byte[] inputBytes) throws IOException { + int length = inputBytes.length; + WrapperInputStream in = new WrapperInputStream(new ByteArrayInputStream(inputBytes)); + byte[] readBytes = new byte[(length / 2) + 1]; + int nread = IOUtils.readNBytes(in, readBytes, 0, readBytes.length); + + int x; + byte[] tmp; + check(nread == readBytes.length, + "Expected number of bytes read: " + readBytes.length + ", got: " + nread); + check(Arrays.equals((tmp = Arrays.copyOf(inputBytes, nread)), readBytes), + "Expected[" + tmp + "], got:[" + readBytes + "]"); + check(!in.isClosed(), "Stream unexpectedly closed"); + + // Read again + nread = IOUtils.readNBytes(in, readBytes, 0, readBytes.length); + + check(nread == length - readBytes.length, + "Expected number of bytes read: " + (length - readBytes.length) + ", got: " + nread); + check(Arrays.equals((tmp = Arrays.copyOfRange(inputBytes, readBytes.length, length)), + Arrays.copyOf(readBytes, nread)), + "Expected[" + tmp + "], got:[" + readBytes + "]"); + // Expect end of stream + check((x = in.read()) == -1, + "Expected end of stream from read(), got " + x); + check((x = in.read(tmp)) == -1, + "Expected end of stream from read(byte[]), got " + x); + check((x = in.read(tmp, 0, tmp.length)) == -1, + "Expected end of stream from read(byte[], int, int), got " + x); + check((x = IOUtils.readNBytes(in, tmp, 0, tmp.length)) == 0, + "Expected end of stream, 0, from readNBytes(byte[], int, int), got " + x); + check(!in.isClosed(), "Stream unexpectedly closed"); + } + + static byte[] createRandomBytes(int size) { + byte[] bytes = new byte[size]; + generator.nextBytes(bytes); + return bytes; + } + + static void check(boolean cond, Object ... failedArgs) { + if (cond) + return; + StringBuilder sb = new StringBuilder(); + for (Object o : failedArgs) + sb.append(o); + throw new RuntimeException(sb.toString()); + } + + + static class WrapperInputStream extends FilterInputStream { + private boolean closed; + WrapperInputStream(InputStream in) { super(in); } + @Override public void close() throws IOException { closed = true; in.close(); } + boolean isClosed() { return closed; } + } +} |