Cherry-pick: [Android WebView] Make InputStreamImpl::Read fill the IOBuffer.

Cherry-pick of chromium crrev.com/24082006

BUG: 10769152

Original description:

[Android WebView] Make InputStreamImpl::Read fill the IOBuffer.

This CL improves the performances of InputStreamImpl::Read. The
previous implementation was limiting each Read to transfer at most
4k. This CL makes it possible to fill, in a single run, the IOBuffer
up to the length provided by the caller.
In practice this will reduces the number of thread hops and IPCs.
Note: the max transfer size, at this point, is imited onlt by the
kMaxAllocationSize constant defined in async_resounrce_handler.cc.

Change-Id: I5c733b845641e98292d78400fefa749c02a8a0ef
diff --git a/android_webview/native/input_stream_impl.cc b/android_webview/native/input_stream_impl.cc
index 24d7be0..fca6a29 100644
--- a/android_webview/native/input_stream_impl.cc
+++ b/android_webview/native/input_stream_impl.cc
@@ -84,46 +84,48 @@
       return false;
   }
 
+  int remaining_length = length;
+  char* dest_write_ptr = dest->data();
   jbyteArray buffer = buffer_.obj();
   *bytes_read = 0;
 
-  const int read_size = std::min(length, kBufferSize);
-  int32_t byte_count;
-  do {
-    // Unfortunately it is valid for the Java InputStream to read 0 bytes some
-    // number of times before returning any more data. Because this method
-    // signals EOF by setting |bytes_read| to 0 and returning true necessary to
-    // call the Java-side read method until it returns something other than 0.
-    byte_count = Java_InputStream_readI_AB_I_I(
-        env, jobject_.obj(), buffer, 0, read_size);
+  while (remaining_length > 0) {
+    const int max_transfer_length = std::min(remaining_length, kBufferSize);
+    const int transfer_length = Java_InputStream_readI_AB_I_I(
+        env, jobject_.obj(), buffer, 0, max_transfer_length);
     if (ClearException(env))
       return false;
-  } while (byte_count == 0);
 
-  // We've reached the end of the stream.
-  if (byte_count < 0)
-    return true;
+    if (transfer_length < 0)  // EOF
+      break;
 
-#ifndef NDEBUG
-  int32_t buffer_length = env->GetArrayLength(buffer);
-  DCHECK_GE(read_size, byte_count);
-  DCHECK_GE(buffer_length, byte_count);
-#endif // NDEBUG
+    // Note: it is possible, yet unlikely, that the Java InputStream returns
+    // a transfer_length == 0 from time to time. In such cases we just continue
+    // the read until we get either valid data or reach EOF.
+    if (transfer_length == 0)
+      continue;
 
-  // The DCHECKs are in place to help Chromium developers in case of bugs,
-  // this check is to prevent a malicious InputStream implementation from
-  // overrunning the |dest| buffer.
-  if (byte_count > read_size)
-    return false;
+    DCHECK_GE(max_transfer_length, transfer_length);
+    DCHECK_GE(env->GetArrayLength(buffer), transfer_length);
 
-  // Copy the data over to the provided C++ side buffer.
-  DCHECK_GE(length, byte_count);
-  env->GetByteArrayRegion(buffer, 0, byte_count,
-      reinterpret_cast<jbyte*>(dest->data() + *bytes_read));
-  if (ClearException(env))
-    return false;
+    // This check is to prevent a malicious InputStream implementation from
+    // overrunning the |dest| buffer.
+    if (transfer_length > max_transfer_length)
+      return false;
 
-  *bytes_read = byte_count;
+    // Copy the data over to the provided C++ IOBuffer.
+    DCHECK_GE(remaining_length, transfer_length);
+    env->GetByteArrayRegion(buffer, 0, transfer_length,
+        reinterpret_cast<jbyte*>(dest_write_ptr));
+    if (ClearException(env))
+      return false;
+
+    remaining_length -= transfer_length;
+    dest_write_ptr += transfer_length;
+  }
+  // bytes_read can be strictly less than the req. length if EOF is encountered.
+  DCHECK(remaining_length >= 0 && remaining_length <= length);
+  *bytes_read = length - remaining_length;
   return true;
 }
 
diff --git a/android_webview/native/input_stream_unittest.cc b/android_webview/native/input_stream_unittest.cc
index 87b3fe3..76ca823 100644
--- a/android_webview/native/input_stream_unittest.cc
+++ b/android_webview/native/input_stream_unittest.cc
@@ -89,10 +89,10 @@
 }
 
 TEST_F(InputStreamTest, TryReadMoreThanBuffer) {
-  const int bytes_requested = 3 * InputStreamImpl::kBufferSize;
+  const int buffer_size = 3 * InputStreamImpl::kBufferSize;
   int bytes_read = 0;
-  DoReadCountedStreamTest(bytes_requested, bytes_requested, &bytes_read);
-  EXPECT_EQ(InputStreamImpl::kBufferSize, bytes_read);
+  DoReadCountedStreamTest(buffer_size, buffer_size * 2, &bytes_read);
+  EXPECT_EQ(buffer_size, bytes_read);
 }
 
 TEST_F(InputStreamTest, CheckContentsReadCorrectly) {
@@ -105,3 +105,17 @@
     EXPECT_EQ(i, (unsigned char)buffer->data()[i]);
   }
 }
+
+TEST_F(InputStreamTest, ReadLargeStreamPartial) {
+  const int bytes_requested = 3 * InputStreamImpl::kBufferSize;
+  int bytes_read = 0;
+  DoReadCountedStreamTest(bytes_requested + 32, bytes_requested, &bytes_read);
+  EXPECT_EQ(bytes_requested, bytes_read);
+}
+
+TEST_F(InputStreamTest, ReadLargeStreamCompletely) {
+  const int bytes_requested = 3 * InputStreamImpl::kBufferSize;
+  int bytes_read = 0;
+  DoReadCountedStreamTest(bytes_requested, bytes_requested, &bytes_read);
+  EXPECT_EQ(bytes_requested, bytes_read);
+}