8193832: Performance of InputStream.readAllBytes() could be improved
authorbpb
Fri, 22 Dec 2017 14:00:03 -0800
changeset 48366 2c1af559e922
parent 48365 0f53d49bb74b
child 48367 3a52333a5e57
8193832: Performance of InputStream.readAllBytes() could be improved Summary: Read into a list of fixed-size buffers which are gathered at the end Reviewed-by: alanb, chegar, plevart, jrose, psandoz
src/java.base/share/classes/java/io/InputStream.java
test/jdk/java/io/InputStream/ReadAllBytes.java
--- a/src/java.base/share/classes/java/io/InputStream.java	Fri Dec 22 15:23:34 2017 -0500
+++ b/src/java.base/share/classes/java/io/InputStream.java	Fri Dec 22 14:00:03 2017 -0800
@@ -25,7 +25,9 @@
 
 package java.io;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -229,30 +231,55 @@
      * @since 9
      */
     public byte[] readAllBytes() throws IOException {
-        byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
-        int capacity = buf.length;
-        int nread = 0;
+        List<byte[]> bufs = null;
+        byte[] result = null;
+        int total = 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)
+        do {
+            byte[] buf = new byte[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) {
                 nread += n;
+            }
 
-            // if the last call to read returned -1, then we're done
-            if (n < 0)
-                break;
+            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 (bufs == null) {
+                        bufs = new ArrayList<>();
+                        bufs.add(result);
+                    }
+                    bufs.add(buf);
+                }
+            }
+        } while (n >= 0); // if the last call to read returned -1, then 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;
+        if (bufs == null) {
+            if (result == null) {
+                return new byte[0];
             }
-            buf = Arrays.copyOf(buf, capacity);
+            return result.length == total ?
+                result : Arrays.copyOf(result, total);
         }
-        return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
+
+        result = new byte[total];
+        int offset = 0;
+        int remaining = total;
+        for (byte[] b : bufs) {
+            int len = Math.min(b.length, remaining);
+            System.arraycopy(b, 0, result, offset, len);
+            offset += len;
+            remaining -= len;
+        }
+
+        return result;
     }
 
     /**
--- a/test/jdk/java/io/InputStream/ReadAllBytes.java	Fri Dec 22 15:23:34 2017 -0500
+++ b/test/jdk/java/io/InputStream/ReadAllBytes.java	Fri Dec 22 14:00:03 2017 -0800
@@ -31,7 +31,7 @@
 
 /*
  * @test
- * @bug 8080835
+ * @bug 8080835 8193832
  * @library /test/lib
  * @build jdk.test.lib.RandomFactory
  * @run main ReadAllBytes
@@ -47,15 +47,11 @@
         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));
+        for (int shift : new int[] {13, 14, 15, 17}) {
+            for (int offset : new int[] {-1, 0, 1}) {
+                test(createRandomBytes((1 << shift) + offset));
+            }
+        }
     }
 
     static void test(byte[] expectedBytes) throws IOException {