8080835: Add blocking bulk read to java.io.InputStream
Reviewed-by: alanb, rriggs, prappo
This commit is contained in:
parent
810d1992d2
commit
46b53cd5cf
@ -25,6 +25,7 @@
|
||||
|
||||
package java.io;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -50,7 +51,7 @@ public abstract class InputStream implements Closeable {
|
||||
// use when skipping.
|
||||
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
|
||||
|
||||
private static final int TRANSFER_BUFFER_SIZE = 8192;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Reads the next byte of data from the input stream. The value byte is
|
||||
@ -191,6 +192,128 @@ public abstract class InputStream implements Closeable {
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*
|
||||
* @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 byte[] readAllBytes() 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 = 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 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 int readNBytes(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 = read(b, off + n, len - n);
|
||||
if (count < 0)
|
||||
break;
|
||||
n += count;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips over and discards <code>n</code> bytes of data from this input
|
||||
* stream. The <code>skip</code> method may, for a variety of reasons, end
|
||||
@ -396,9 +519,9 @@ public abstract class InputStream implements Closeable {
|
||||
public long transferTo(OutputStream out) throws IOException {
|
||||
Objects.requireNonNull(out, "out");
|
||||
long transferred = 0;
|
||||
byte[] buffer = new byte[TRANSFER_BUFFER_SIZE];
|
||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
int read;
|
||||
while ((read = this.read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) {
|
||||
while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
|
||||
out.write(buffer, 0, read);
|
||||
transferred += read;
|
||||
}
|
||||
|
104
jdk/test/java/io/InputStream/ReadAllBytes.java
Normal file
104
jdk/test/java/io/InputStream/ReadAllBytes.java
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8080835
|
||||
* @library /lib/testlibrary
|
||||
* @build jdk.testlibrary.*
|
||||
* @run main ReadAllBytes
|
||||
* @summary Basic test for InputStream.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 = in.readAllBytes();
|
||||
|
||||
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(in.readAllBytes().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; }
|
||||
}
|
||||
}
|
116
jdk/test/java/io/InputStream/ReadNBytes.java
Normal file
116
jdk/test/java/io/InputStream/ReadNBytes.java
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8080835
|
||||
* @library /lib/testlibrary
|
||||
* @build jdk.testlibrary.*
|
||||
* @run main ReadNBytes
|
||||
* @summary Basic test for InputStream.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 = in.readNBytes(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 = in.readNBytes(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 = in.readNBytes(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; }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user