8139206: Add InputStream readNBytes(int len)
Reviewed-by: alanb, chegar, plevart, apetcher, rriggs, weijun
This commit is contained in:
parent
482ff9cfdf
commit
697c3fc073
@ -63,7 +63,8 @@ public abstract class InputStream implements Closeable {
|
||||
*
|
||||
* <p> While the stream is open, the {@code available()}, {@code read()},
|
||||
* {@code read(byte[])}, {@code read(byte[], int, int)},
|
||||
* {@code readAllBytes()}, {@code readNBytes()}, {@code skip()}, and
|
||||
* {@code readAllBytes()}, {@code readNBytes(byte[], int, int)},
|
||||
* {@code readNBytes(int)}, {@code skip(long)}, and
|
||||
* {@code transferTo()} methods all behave as if end of stream has been
|
||||
* reached. After the stream has been closed, these methods all throw
|
||||
* {@code IOException}.
|
||||
@ -122,6 +123,15 @@ public abstract class InputStream implements Closeable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readNBytes(int len) throws IOException {
|
||||
if (len < 0) {
|
||||
throw new IllegalArgumentException("len < 0");
|
||||
}
|
||||
ensureOpen();
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
ensureOpen();
|
||||
@ -233,8 +243,8 @@ public abstract class InputStream implements Closeable {
|
||||
* <code>b</code> and the number of bytes read before the exception
|
||||
* occurred is returned. The default implementation of this method blocks
|
||||
* until the requested amount of input data <code>len</code> has been read,
|
||||
* end of file is detected, or an exception is thrown. Subclasses are encouraged
|
||||
* to provide a more efficient implementation of this method.
|
||||
* end of file is detected, or an exception is thrown. Subclasses are
|
||||
* encouraged to provide a more efficient implementation of this method.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset in array <code>b</code>
|
||||
@ -308,26 +318,85 @@ public abstract class InputStream implements Closeable {
|
||||
* 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}.
|
||||
*
|
||||
* @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.
|
||||
* allocated.
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public byte[] readAllBytes() throws IOException {
|
||||
return readNBytes(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 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 byte[] readNBytes(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[DEFAULT_BUFFER_SIZE];
|
||||
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 = read(buf, nread, buf.length - nread)) > 0) {
|
||||
while ((n = read(buf, nread,
|
||||
Math.min(buf.length - nread, remaining))) > 0) {
|
||||
nread += n;
|
||||
remaining -= n;
|
||||
}
|
||||
|
||||
if (nread > 0) {
|
||||
@ -345,7 +414,9 @@ public abstract class InputStream implements Closeable {
|
||||
bufs.add(buf);
|
||||
}
|
||||
}
|
||||
} while (n >= 0); // if the last call to read returned -1, then break
|
||||
// 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) {
|
||||
@ -357,12 +428,12 @@ public abstract class InputStream implements Closeable {
|
||||
|
||||
result = new byte[total];
|
||||
int offset = 0;
|
||||
int remaining = total;
|
||||
remaining = total;
|
||||
for (byte[] b : bufs) {
|
||||
int len = Math.min(b.length, remaining);
|
||||
System.arraycopy(b, 0, result, offset, len);
|
||||
offset += len;
|
||||
remaining -= len;
|
||||
int count = Math.min(b.length, remaining);
|
||||
System.arraycopy(b, 0, result, offset, count);
|
||||
offset += count;
|
||||
remaining -= count;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -31,7 +31,7 @@ import static org.testng.Assert.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4358774
|
||||
* @bug 4358774 8139206
|
||||
* @run testng NullInputStream
|
||||
* @summary Check for expected behavior of InputStream.nullInputStream().
|
||||
*/
|
||||
@ -107,7 +107,7 @@ public class NullInputStream {
|
||||
}
|
||||
|
||||
@Test(groups = "open")
|
||||
public static void testreadNBytes() {
|
||||
public static void testReadNBytes() {
|
||||
try {
|
||||
assertEquals(0, openStream.readNBytes(new byte[1], 0, 1),
|
||||
"readNBytes(byte[],int,int) != 0");
|
||||
@ -116,6 +116,26 @@ public class NullInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = "open")
|
||||
public static void testReadNBytesWithLength() {
|
||||
try {
|
||||
assertEquals(0, openStream.readNBytes(-1).length,
|
||||
"readNBytes(-1) != 0");
|
||||
fail("Expected IllegalArgumentException not thrown");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
} catch (IOException ioe) {
|
||||
fail("Unexpected IOException");
|
||||
}
|
||||
try {
|
||||
assertEquals(0, openStream.readNBytes(0).length,
|
||||
"readNBytes(0, false) != 0");
|
||||
assertEquals(0, openStream.readNBytes(1).length,
|
||||
"readNBytes(1, false) != 0");
|
||||
} catch (IOException ioe) {
|
||||
fail("Unexpected IOException");
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = "open")
|
||||
public static void testSkip() {
|
||||
try {
|
||||
@ -180,6 +200,15 @@ public class NullInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = "closed")
|
||||
public static void testReadNBytesWithLengthClosed() {
|
||||
try {
|
||||
closedStream.readNBytes(1);
|
||||
fail("Expected IOException not thrown");
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = "closed")
|
||||
public static void testSkipClosed() {
|
||||
try {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2018, 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
|
||||
@ -31,7 +31,7 @@ import jdk.test.lib.RandomFactory;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8080835
|
||||
* @bug 8080835 8139206
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.RandomFactory
|
||||
* @run main ReadNBytes
|
||||
@ -46,15 +46,19 @@ public class ReadNBytes {
|
||||
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));
|
||||
for (int shift : new int[] {13, 15, 17}) {
|
||||
for (int offset : new int[] {-1, 0, 1}) {
|
||||
test(createRandomBytes((1 << shift) + offset));
|
||||
}
|
||||
}
|
||||
|
||||
test(-1);
|
||||
test(0);
|
||||
for (int shift : new int[] {13, 15, 17}) {
|
||||
for (int offset : new int[] {-1, 0, 1}) {
|
||||
test((1 << shift) + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test(byte[] inputBytes) throws IOException {
|
||||
@ -91,6 +95,46 @@ public class ReadNBytes {
|
||||
check(!in.isClosed(), "Stream unexpectedly closed");
|
||||
}
|
||||
|
||||
static void test(int max) throws IOException {
|
||||
byte[] inputBytes = max <= 0 ? new byte[0] : createRandomBytes(max);
|
||||
WrapperInputStream in =
|
||||
new WrapperInputStream(new ByteArrayInputStream(inputBytes));
|
||||
|
||||
if (max < 0) {
|
||||
try {
|
||||
in.readNBytes(max);
|
||||
check(false, "Expected IllegalArgumentException not thrown");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
return;
|
||||
}
|
||||
} else if (max == 0) {
|
||||
int x;
|
||||
check((x = in.readNBytes(max).length) == 0,
|
||||
"Expected zero bytes, got " + x);
|
||||
return;
|
||||
}
|
||||
|
||||
int off = Math.toIntExact(in.skip(generator.nextInt(max/2)));
|
||||
int len = generator.nextInt(max - 1 - off);
|
||||
byte[] readBytes = in.readNBytes(len);
|
||||
check(readBytes.length == len,
|
||||
"Expected " + len + " bytes, got " + readBytes.length);
|
||||
check(Arrays.equals(inputBytes, off, off + len, readBytes, 0, len),
|
||||
"Expected[" + Arrays.copyOfRange(inputBytes, off, off + len) +
|
||||
"], got:[" + readBytes + "]");
|
||||
|
||||
int remaining = max - (off + len);
|
||||
readBytes = in.readNBytes(remaining);
|
||||
check(readBytes.length == remaining,
|
||||
"Expected " + remaining + "bytes, got " + readBytes.length);
|
||||
check(Arrays.equals(inputBytes, off + len, max,
|
||||
readBytes, 0, remaining),
|
||||
"Expected[" + Arrays.copyOfRange(inputBytes, off + len, max) +
|
||||
"], got:[" + readBytes + "]");
|
||||
|
||||
check(!in.isClosed(), "Stream unexpectedly closed");
|
||||
}
|
||||
|
||||
static byte[] createRandomBytes(int size) {
|
||||
byte[] bytes = new byte[size];
|
||||
generator.nextBytes(bytes);
|
||||
|
Loading…
x
Reference in New Issue
Block a user