file.stream() is slow and CPU-bound
https://bugs.webkit.org/show_bug.cgi?id=235448

Reviewed by Alex Christensen.

We introduce a ReadAsBinaryChunks mode to prevent storing the whole file in memory.
Use this in Blob stream implementation.
The new implementation is much faster for big files given it does not need to keep in memory the whole file.
Covered by existing tests.

* fileapi/Blob.cpp:
* fileapi/FileReaderLoader.cpp:
* fileapi/FileReaderLoader.h:
* fileapi/FileReaderLoaderClient.h:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@288463 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 84e12dc..d7fd122 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,5 +1,22 @@
 2022-01-24  Youenn Fablet  <youenn@apple.com>
 
+        file.stream() is slow and CPU-bound
+        https://bugs.webkit.org/show_bug.cgi?id=235448
+
+        Reviewed by Alex Christensen.
+
+        We introduce a ReadAsBinaryChunks mode to prevent storing the whole file in memory.
+        Use this in Blob stream implementation.
+        The new implementation is much faster for big files given it does not need to keep in memory the whole file.
+        Covered by existing tests.
+
+        * fileapi/Blob.cpp:
+        * fileapi/FileReaderLoader.cpp:
+        * fileapi/FileReaderLoader.h:
+        * fileapi/FileReaderLoaderClient.h:
+
+2022-01-24  Youenn Fablet  <youenn@apple.com>
+
         [ iOS Release ] fast/mediastream/mediaElement-gc.html is a flaky failure
         https://bugs.webkit.org/show_bug.cgi?id=231601
         <rdar://problem/84158942>
diff --git a/Source/WebCore/fileapi/Blob.cpp b/Source/WebCore/fileapi/Blob.cpp
index 4dbec12..23608cb 100644
--- a/Source/WebCore/fileapi/Blob.cpp
+++ b/Source/WebCore/fileapi/Blob.cpp
@@ -241,7 +241,7 @@
     class BlobStreamSource : public FileReaderLoaderClient, public ReadableStreamSource {
     public:
         BlobStreamSource(ScriptExecutionContext& scriptExecutionContext, Blob& blob)
-            : m_loader(makeUniqueRef<FileReaderLoader>(FileReaderLoader::ReadType::ReadAsArrayBuffer, this))
+            : m_loader(makeUniqueRef<FileReaderLoader>(FileReaderLoader::ReadType::ReadAsBinaryChunks, this))
         {
             m_loader->start(&scriptExecutionContext, blob);
         }
@@ -265,19 +265,11 @@
 
         // FileReaderLoaderClient
         void didStartLoading() final { }
-        void didReceiveData() final
+        void didReceiveData() final { }
+        void didReceiveBinaryChunk(const SharedBuffer& buffer) final
         {
-            auto result = m_loader->arrayBufferResult();
-            if (!result)
-                return;
-
-            if (m_loader->isCompleted() && !m_bytesRead)
-                controller().enqueue(WTFMove(result));
-            else {
-                auto bytesLoaded = m_loader->bytesLoaded();
-                controller().enqueue(result->slice(m_bytesRead, bytesLoaded));
-                m_bytesRead = bytesLoaded;
-            }
+            if (!controller().enqueue(buffer.tryCreateArrayBuffer()))
+                doCancel();
         }
         void didFinishLoading() final
         {
@@ -294,7 +286,6 @@
         }
 
         UniqueRef<FileReaderLoader> m_loader;
-        size_t m_bytesRead { 0 };
         bool m_isStarted { false };
         std::optional<Exception> m_exception;
     };
diff --git a/Source/WebCore/fileapi/FileReaderLoader.cpp b/Source/WebCore/fileapi/FileReaderLoader.cpp
index 8e030ed..1cb49c6 100644
--- a/Source/WebCore/fileapi/FileReaderLoader.cpp
+++ b/Source/WebCore/fileapi/FileReaderLoader.cpp
@@ -177,6 +177,12 @@
     if (m_errorCode)
         return;
 
+    if (m_readType == ReadType::ReadAsBinaryChunks) {
+        if (m_client)
+            m_client->didReceiveBinaryChunk(buffer);
+        return;
+    }
+
     int length = buffer.size();
     unsigned remainingBufferSpace = m_totalBytes - m_bytesLoaded;
     if (length > static_cast<long long>(remainingBufferSpace)) {
@@ -311,7 +317,8 @@
         if (isCompleted())
             convertToDataURL();
         break;
-    default:
+    case ReadAsBlob:
+    case ReadAsBinaryChunks:
         ASSERT_NOT_REACHED();
     }
     
diff --git a/Source/WebCore/fileapi/FileReaderLoader.h b/Source/WebCore/fileapi/FileReaderLoader.h
index 382b206..9cc5cec 100644
--- a/Source/WebCore/fileapi/FileReaderLoader.h
+++ b/Source/WebCore/fileapi/FileReaderLoader.h
@@ -58,7 +58,8 @@
         ReadAsBinaryString,
         ReadAsBlob,
         ReadAsText,
-        ReadAsDataURL
+        ReadAsDataURL,
+        ReadAsBinaryChunks
     };
 
     // If client is given, do the loading asynchronously. Otherwise, load synchronously.
diff --git a/Source/WebCore/fileapi/FileReaderLoaderClient.h b/Source/WebCore/fileapi/FileReaderLoaderClient.h
index bff3e36..0478b55 100644
--- a/Source/WebCore/fileapi/FileReaderLoaderClient.h
+++ b/Source/WebCore/fileapi/FileReaderLoaderClient.h
@@ -41,6 +41,7 @@
 
     virtual void didStartLoading() = 0;
     virtual void didReceiveData() = 0;
+    virtual void didReceiveBinaryChunk(const SharedBuffer&) { }
     virtual void didFinishLoading() = 0;
     virtual void didFail(ExceptionCode errorCode) = 0;
 };