提交 f9abd2ba 编写于 作者: A andrew

8139206: Add InputStream readNBytes(int len)

Reviewed-by: mbalao
上级 ae3dda4e
...@@ -92,24 +92,6 @@ public class IOUtils { ...@@ -92,24 +92,6 @@ public class IOUtils {
return output; return output;
} }
/**
* Read {@code length} of bytes from {@code in}. 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, must not be negative
* @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}.
*/
public static byte[] readNBytes(InputStream is, int length) throws IOException {
if (length < 0) {
throw new IOException("length cannot be negative: " + length);
}
return readFully(is, length, true);
}
/** /**
* Reads all remaining bytes from the input stream. This method blocks until * 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 * all remaining bytes have been read and end of stream is detected, or an
...@@ -132,27 +114,87 @@ public class IOUtils { ...@@ -132,27 +114,87 @@ public class IOUtils {
* It is strongly recommended that the stream be promptly closed if an I/O * It is strongly recommended that the stream be promptly closed if an I/O
* error occurs. * 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 * @param is input stream, must not be null
* @return a byte array containing the bytes read from this input stream * @return a byte array containing the bytes read from this input stream
* @throws IOException if an I/O error occurs * @throws IOException if an I/O error occurs
* @throws OutOfMemoryError if an array of the required size cannot be * @throws OutOfMemoryError if an array of the required size cannot be
* allocated. For example, if an array larger than {@code 2GB} would * allocated.
* be required to store the bytes.
* *
* @since 1.9 * @since 1.9
*/ */
public static byte[] readAllBytes(InputStream is) throws IOException { 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; List<byte[]> bufs = null;
byte[] result = null; byte[] result = null;
int total = 0; int total = 0;
int remaining = len;
int n; int n;
do { do {
byte[] buf = new byte[DEFAULT_BUFFER_SIZE]; byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
int nread = 0; int nread = 0;
// read to EOF which may read more or less than buffer size // read to EOF which may read more or less than buffer size
while ((n = is.read(buf, nread, buf.length - nread)) > 0) { while ((n = is.read(buf, nread,
Math.min(buf.length - nread, remaining))) > 0) {
nread += n; nread += n;
remaining -= n;
} }
if (nread > 0) { if (nread > 0) {
...@@ -170,7 +212,9 @@ public class IOUtils { ...@@ -170,7 +212,9 @@ public class IOUtils {
bufs.add(buf); 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 (bufs == null) {
if (result == null) { if (result == null) {
...@@ -182,12 +226,12 @@ public class IOUtils { ...@@ -182,12 +226,12 @@ public class IOUtils {
result = new byte[total]; result = new byte[total];
int offset = 0; int offset = 0;
int remaining = total; remaining = total;
for (byte[] b : bufs) { for (byte[] b : bufs) {
int len = Math.min(b.length, remaining); int count = Math.min(b.length, remaining);
System.arraycopy(b, 0, result, offset, len); System.arraycopy(b, 0, result, offset, count);
offset += len; offset += count;
remaining -= len; remaining -= count;
} }
return result; return result;
......
/* /*
* Copyright (c) 2015, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -33,7 +33,7 @@ import sun.misc.IOUtils; ...@@ -33,7 +33,7 @@ import sun.misc.IOUtils;
/* /*
* @test * @test
* @bug 8080835 * @bug 8080835 8139206
* @library /lib/testlibrary * @library /lib/testlibrary
* @build jdk.testlibrary.* * @build jdk.testlibrary.*
* @run main ReadNBytes * @run main ReadNBytes
...@@ -48,15 +48,19 @@ public class ReadNBytes { ...@@ -48,15 +48,19 @@ public class ReadNBytes {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
test(new byte[]{1, 2, 3}); test(new byte[]{1, 2, 3});
test(createRandomBytes(1024)); test(createRandomBytes(1024));
test(createRandomBytes((1 << 13) - 1)); for (int shift : new int[] {13, 15, 17}) {
test(createRandomBytes((1 << 13))); for (int offset : new int[] {-1, 0, 1}) {
test(createRandomBytes((1 << 13) + 1)); test(createRandomBytes((1 << shift) + offset));
test(createRandomBytes((1 << 15) - 1)); }
test(createRandomBytes((1 << 15))); }
test(createRandomBytes((1 << 15) + 1));
test(createRandomBytes((1 << 17) - 1)); test(-1);
test(createRandomBytes((1 << 17))); test(0);
test(createRandomBytes((1 << 17) + 1)); 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 { static void test(byte[] inputBytes) throws IOException {
...@@ -93,6 +97,48 @@ public class ReadNBytes { ...@@ -93,6 +97,48 @@ public class ReadNBytes {
check(!in.isClosed(), "Stream unexpectedly closed"); check(!in.isClosed(), "Stream unexpectedly closed");
} }
static void test(int max) throws IOException {
byte[] subset1, subset2;
byte[] inputBytes = max <= 0 ? new byte[0] : createRandomBytes(max);
WrapperInputStream in =
new WrapperInputStream(new ByteArrayInputStream(inputBytes));
if (max < 0) {
try {
IOUtils.readNBytes(in, max);
check(false, "Expected IllegalArgumentException not thrown");
} catch (IllegalArgumentException iae) {
return;
}
} else if (max == 0) {
int x;
check((x = IOUtils.readNBytes(in, 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 = IOUtils.readNBytes(in, len);
check(readBytes.length == len,
"Expected " + len + " bytes, got " + readBytes.length);
subset1 = Arrays.copyOfRange(inputBytes, off, off + len);
subset2 = Arrays.copyOfRange(readBytes, 0, len);
check(Arrays.equals(subset1, subset2), "Expected[" + subset1 +
"], got:[" + readBytes + "]");
int remaining = max - (off + len);
readBytes = IOUtils.readNBytes(in, remaining);
check(readBytes.length == remaining,
"Expected " + remaining + "bytes, got " + readBytes.length);
subset1 = Arrays.copyOfRange(inputBytes, off + len, max);
subset2 = Arrays.copyOfRange(readBytes, 0, remaining);
check(Arrays.equals(subset1, subset2), "Expected[" + subset1 +
"], got:[" + readBytes + "]");
check(!in.isClosed(), "Stream unexpectedly closed");
}
static byte[] createRandomBytes(int size) { static byte[] createRandomBytes(int size) {
byte[] bytes = new byte[size]; byte[] bytes = new byte[size];
generator.nextBytes(bytes); generator.nextBytes(bytes);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册