<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[182856] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/182856">182856</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2015-04-15 12:54:21 -0700 (Wed, 15 Apr 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Network Cache: Inline small body data to record file
https://bugs.webkit.org/show_bug.cgi?id=143783

Reviewed by Chris Dumez.

We currently save all body data as separate files. We can improve space efficiency and do less reads and writes
by inlining smaller resource bodies with the header.

* NetworkProcess/cache/NetworkCacheIOChannel.h:
* NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm:
(WebKit::NetworkCache::IOChannel::read):
(WebKit::NetworkCache::IOChannel::readSync):
(WebKit::NetworkCache::IOChannel::write):

    Add WorkQueue argument to allow specifying which queue the result is submitted to.

* NetworkProcess/cache/NetworkCacheStorage.cpp:
(WebKit::NetworkCache::decodeRecordMetaData):

    Add a boolean indicating whether the body is inlined.

(WebKit::NetworkCache::decodeRecordHeader):
(WebKit::NetworkCache::Storage::decodeRecord):
(WebKit::NetworkCache::encodeRecordMetaData):
(WebKit::NetworkCache::Storage::storeBodyAsBlob):
(WebKit::NetworkCache::Storage::encodeRecord):
(WebKit::NetworkCache::Storage::dispatchReadOperation):

    Read the record first, then read the blob if needed.
    Submit the read operation directly from the main queue. Only thing we do is opening an IO channel
    and that uses O_NONBLOCK.
    Process the read results in the IO work queue where we now do the blob retrieval.

(WebKit::NetworkCache::shouldStoreBodyAsBlob):

    The current threshold for saving a separate blob is 16KB.

(WebKit::NetworkCache::Storage::dispatchWriteOperation):
(WebKit::NetworkCache::Storage::traverse):
(WebKit::NetworkCache::createRecord): Deleted.
(WebKit::NetworkCache::encodeRecordHeader): Deleted.
* NetworkProcess/cache/NetworkCacheStorage.h:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2NetworkProcesscacheNetworkCacheIOChannelh">trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h</a></li>
<li><a href="#trunkSourceWebKit2NetworkProcesscacheNetworkCacheIOChannelCocoamm">trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm</a></li>
<li><a href="#trunkSourceWebKit2NetworkProcesscacheNetworkCacheStoragecpp">trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp</a></li>
<li><a href="#trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageh">trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (182855 => 182856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-04-15 19:43:22 UTC (rev 182855)
+++ trunk/Source/WebKit2/ChangeLog        2015-04-15 19:54:21 UTC (rev 182856)
</span><span class="lines">@@ -1,3 +1,48 @@
</span><ins>+2015-04-15  Antti Koivisto  &lt;antti@apple.com&gt;
+
+        Network Cache: Inline small body data to record file
+        https://bugs.webkit.org/show_bug.cgi?id=143783
+
+        Reviewed by Chris Dumez.
+
+        We currently save all body data as separate files. We can improve space efficiency and do less reads and writes
+        by inlining smaller resource bodies with the header.
+
+        * NetworkProcess/cache/NetworkCacheIOChannel.h:
+        * NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm:
+        (WebKit::NetworkCache::IOChannel::read):
+        (WebKit::NetworkCache::IOChannel::readSync):
+        (WebKit::NetworkCache::IOChannel::write):
+
+            Add WorkQueue argument to allow specifying which queue the result is submitted to.
+
+        * NetworkProcess/cache/NetworkCacheStorage.cpp:
+        (WebKit::NetworkCache::decodeRecordMetaData):
+
+            Add a boolean indicating whether the body is inlined.
+
+        (WebKit::NetworkCache::decodeRecordHeader):
+        (WebKit::NetworkCache::Storage::decodeRecord):
+        (WebKit::NetworkCache::encodeRecordMetaData):
+        (WebKit::NetworkCache::Storage::storeBodyAsBlob):
+        (WebKit::NetworkCache::Storage::encodeRecord):
+        (WebKit::NetworkCache::Storage::dispatchReadOperation):
+
+            Read the record first, then read the blob if needed.
+            Submit the read operation directly from the main queue. Only thing we do is opening an IO channel
+            and that uses O_NONBLOCK.
+            Process the read results in the IO work queue where we now do the blob retrieval.
+
+        (WebKit::NetworkCache::shouldStoreBodyAsBlob):
+
+            The current threshold for saving a separate blob is 16KB.
+
+        (WebKit::NetworkCache::Storage::dispatchWriteOperation):
+        (WebKit::NetworkCache::Storage::traverse):
+        (WebKit::NetworkCache::createRecord): Deleted.
+        (WebKit::NetworkCache::encodeRecordHeader): Deleted.
+        * NetworkProcess/cache/NetworkCacheStorage.h:
+
</ins><span class="cx"> 2015-04-15  Tim Horton  &lt;timothy_horton@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Non-local keyboards don't update scroll view parameters
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheIOChannelh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h (182855 => 182856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h        2015-04-15 19:43:22 UTC (rev 182855)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h        2015-04-15 19:54:21 UTC (rev 182856)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> #include &quot;NetworkCacheData.h&quot;
</span><span class="cx"> #include &lt;functional&gt;
</span><span class="cx"> #include &lt;wtf/ThreadSafeRefCounted.h&gt;
</span><ins>+#include &lt;wtf/WorkQueue.h&gt;
</ins><span class="cx"> #include &lt;wtf/text/WTFString.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="lines">@@ -41,9 +42,11 @@
</span><span class="cx">     enum class Type { Read, Write, Create };
</span><span class="cx">     static Ref&lt;IOChannel&gt; open(const String&amp; file, Type);
</span><span class="cx"> 
</span><del>-    void read(size_t offset, size_t, std::function&lt;void (Data&amp;, int error)&gt;);
-    void readSync(size_t offset, size_t, std::function&lt;void (Data&amp;, int error)&gt;);
-    void write(size_t offset, const Data&amp;, std::function&lt;void (int error)&gt;);
</del><ins>+    // Using nullptr as queue submits the result to the main queue.
+    // FIXME: We should add WorkQueue::main() instead.
+    void read(size_t offset, size_t, WorkQueue*, std::function&lt;void (Data&amp;, int error)&gt;);
+    void readSync(size_t offset, size_t, WorkQueue*, std::function&lt;void (Data&amp;, int error)&gt;);
+    void write(size_t offset, const Data&amp;, WorkQueue*, std::function&lt;void (int error)&gt;);
</ins><span class="cx"> 
</span><span class="cx">     const String&amp; path() const { return m_path; }
</span><span class="cx">     Type type() const { return m_type; }
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheIOChannelCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm (182855 => 182856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm        2015-04-15 19:43:22 UTC (rev 182855)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelCocoa.mm        2015-04-15 19:54:21 UTC (rev 182856)
</span><span class="lines">@@ -79,11 +79,12 @@
</span><span class="cx">     return adoptRef(*new IOChannel(filePath, type));
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void IOChannel::read(size_t offset, size_t size, std::function&lt;void (Data&amp;, int error)&gt; completionHandler)
</del><ins>+void IOChannel::read(size_t offset, size_t size, WorkQueue* queue, std::function&lt;void (Data&amp;, int error)&gt; completionHandler)
</ins><span class="cx"> {
</span><span class="cx">     RefPtr&lt;IOChannel&gt; channel(this);
</span><span class="cx">     bool didCallCompletionHandler = false;
</span><del>-    dispatch_io_read(m_dispatchIO.get(), offset, size, dispatch_get_main_queue(), [channel, completionHandler, didCallCompletionHandler](bool done, dispatch_data_t fileData, int error) mutable {
</del><ins>+    auto dispatchQueue = queue ? queue-&gt;dispatchQueue() : dispatch_get_main_queue();
+    dispatch_io_read(m_dispatchIO.get(), offset, size, dispatchQueue, [channel, completionHandler, didCallCompletionHandler](bool done, dispatch_data_t fileData, int error) mutable {
</ins><span class="cx">         ASSERT_UNUSED(done, done || !didCallCompletionHandler);
</span><span class="cx">         if (didCallCompletionHandler)
</span><span class="cx">             return;
</span><span class="lines">@@ -95,21 +96,22 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // FIXME: It would be better to do without this.
</span><del>-void IOChannel::readSync(size_t offset, size_t size, std::function&lt;void (Data&amp;, int error)&gt; completionHandler)
</del><ins>+void IOChannel::readSync(size_t offset, size_t size, WorkQueue* queue, std::function&lt;void (Data&amp;, int error)&gt; completionHandler)
</ins><span class="cx"> {
</span><span class="cx">     auto semaphore = adoptDispatch(dispatch_semaphore_create(0));
</span><del>-    read(offset, size, [semaphore, &amp;completionHandler](Data&amp; data, int error) {
</del><ins>+    read(offset, size, queue, [semaphore, &amp;completionHandler](Data&amp; data, int error) {
</ins><span class="cx">         completionHandler(data, error);
</span><span class="cx">         dispatch_semaphore_signal(semaphore.get());
</span><span class="cx">     });
</span><span class="cx">     dispatch_semaphore_wait(semaphore.get(), DISPATCH_TIME_FOREVER);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void IOChannel::write(size_t offset, const Data&amp; data, std::function&lt;void (int error)&gt; completionHandler)
</del><ins>+void IOChannel::write(size_t offset, const Data&amp; data, WorkQueue* queue, std::function&lt;void (int error)&gt; completionHandler)
</ins><span class="cx"> {
</span><span class="cx">     RefPtr&lt;IOChannel&gt; channel(this);
</span><span class="cx">     auto dispatchData = data.dispatchData();
</span><del>-    dispatch_io_write(m_dispatchIO.get(), offset, dispatchData, dispatch_get_main_queue(), [channel, completionHandler](bool done, dispatch_data_t fileData, int error) {
</del><ins>+    auto dispatchQueue = queue ? queue-&gt;dispatchQueue() : dispatch_get_main_queue();
+    dispatch_io_write(m_dispatchIO.get(), offset, dispatchData, dispatchQueue, [channel, completionHandler](bool done, dispatch_data_t fileData, int error) {
</ins><span class="cx">         ASSERT_UNUSED(done, done);
</span><span class="cx">         completionHandler(error);
</span><span class="cx">     });
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheStoragecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp (182855 => 182856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp        2015-04-15 19:43:22 UTC (rev 182855)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp        2015-04-15 19:54:21 UTC (rev 182856)
</span><span class="lines">@@ -225,6 +225,7 @@
</span><span class="cx">     uint64_t headerSize;
</span><span class="cx">     SHA1::Digest bodyHash;
</span><span class="cx">     uint64_t bodySize;
</span><ins>+    bool isBodyInline;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> static bool decodeRecordMetaData(RecordMetaData&amp; metaData, const Data&amp; fileData)
</span><span class="lines">@@ -246,6 +247,8 @@
</span><span class="cx">             return false;
</span><span class="cx">         if (!decoder.decode(metaData.bodySize))
</span><span class="cx">             return false;
</span><ins>+        if (!decoder.decode(metaData.isBodyInline))
+            return false;
</ins><span class="cx">         if (!decoder.verifyChecksum())
</span><span class="cx">             return false;
</span><span class="cx">         metaData.headerOffset = decoder.currentOffset();
</span><span class="lines">@@ -255,7 +258,7 @@
</span><span class="cx">     return success;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool decodeRecordHeader(const Data&amp; fileData, RecordMetaData&amp; metaData, Data&amp; data)
</del><ins>+static bool decodeRecordHeader(const Data&amp; fileData, RecordMetaData&amp; metaData, Data&amp; headerData)
</ins><span class="cx"> {
</span><span class="cx">     if (!decodeRecordMetaData(metaData, fileData)) {
</span><span class="cx">         LOG(NetworkCacheStorage, &quot;(NetworkProcess) meta data decode failure&quot;);
</span><span class="lines">@@ -267,17 +270,18 @@
</span><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    auto headerData = fileData.subrange(metaData.headerOffset, metaData.headerSize);
</del><ins>+    headerData = fileData.subrange(metaData.headerOffset, metaData.headerSize);
</ins><span class="cx">     if (metaData.headerChecksum != hashData(headerData)) {
</span><span class="cx">         LOG(NetworkCacheStorage, &quot;(NetworkProcess) header checksum mismatch&quot;);
</span><span class="cx">         return false;
</span><span class="cx">     }
</span><del>-    data = { headerData };
</del><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static std::unique_ptr&lt;Storage::Record&gt; createRecord(const Data&amp; recordData, const BlobStorage::Blob&amp; bodyBlob, const Key&amp; key)
</del><ins>+std::unique_ptr&lt;Storage::Record&gt; Storage::decodeRecord(const Data&amp; recordData, const Key&amp; key)
</ins><span class="cx"> {
</span><ins>+    ASSERT(!RunLoop::isMain());
+
</ins><span class="cx">     RecordMetaData metaData;
</span><span class="cx">     Data headerData;
</span><span class="cx">     if (!decodeRecordHeader(recordData, metaData, headerData))
</span><span class="lines">@@ -290,16 +294,30 @@
</span><span class="cx">     auto timeStamp = std::chrono::system_clock::time_point(metaData.epochRelativeTimeStamp);
</span><span class="cx">     if (timeStamp &gt; std::chrono::system_clock::now())
</span><span class="cx">         return nullptr;
</span><del>-    if (metaData.bodySize != bodyBlob.data.size())
-        return nullptr;
-    if (metaData.bodyHash != bodyBlob.hash)
-        return nullptr;
</del><span class="cx"> 
</span><ins>+    Data bodyData;
+    if (metaData.isBodyInline) {
+        size_t bodyOffset = metaData.headerOffset + headerData.size();
+        if (bodyOffset + metaData.bodySize != recordData.size())
+            return nullptr;
+        bodyData = recordData.subrange(bodyOffset, metaData.bodySize);
+        if (metaData.bodyHash != computeSHA1(bodyData))
+            return nullptr;
+    } else {
+        auto bodyPath = bodyPathForKey(key, recordsPath());
+        auto bodyBlob = m_blobStorage.get(bodyPath);
+        if (metaData.bodySize != bodyBlob.data.size())
+            return nullptr;
+        if (metaData.bodyHash != bodyBlob.hash)
+            return nullptr;
+        bodyData = bodyBlob.data;
+    }
+
</ins><span class="cx">     return std::make_unique&lt;Storage::Record&gt;(Storage::Record {
</span><span class="cx">         metaData.key,
</span><span class="cx">         timeStamp,
</span><span class="cx">         headerData,
</span><del>-        bodyBlob.data
</del><ins>+        bodyData
</ins><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -314,23 +332,49 @@
</span><span class="cx">     encoder &lt;&lt; metaData.headerSize;
</span><span class="cx">     encoder &lt;&lt; metaData.bodyHash;
</span><span class="cx">     encoder &lt;&lt; metaData.bodySize;
</span><ins>+    encoder &lt;&lt; metaData.isBodyInline;
</ins><span class="cx"> 
</span><span class="cx">     encoder.encodeChecksum();
</span><span class="cx"> 
</span><span class="cx">     return Data(encoder.buffer(), encoder.bufferSize());
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static Data encodeRecordHeader(const Storage::Record&amp; record, SHA1::Digest bodyHash)
</del><ins>+Optional&lt;BlobStorage::Blob&gt; Storage::storeBodyAsBlob(const Record&amp; record, const MappedBodyHandler&amp; mappedBodyHandler)
</ins><span class="cx"> {
</span><ins>+    auto bodyPath = bodyPathForKey(record.key, recordsPath());
+
+    // Store the body.
+    auto blob = m_blobStorage.add(bodyPath, record.body);
+    if (blob.data.isNull())
+        return { };
+
+    // Tell the client we now have a disk-backed map for this data.
+    if (mappedBodyHandler) {
+        RunLoop::main().dispatch([blob, mappedBodyHandler] {
+            mappedBodyHandler(blob.data);
+        });
+    }
+    return blob;
+}
+
+Data Storage::encodeRecord(const Record&amp; record, Optional&lt;BlobStorage::Blob&gt; blob)
+{
+    ASSERT(!blob || bytesEqual(blob.value().data, record.body));
+
</ins><span class="cx">     RecordMetaData metaData(record.key);
</span><span class="cx">     metaData.epochRelativeTimeStamp = std::chrono::duration_cast&lt;std::chrono::milliseconds&gt;(record.timeStamp.time_since_epoch());
</span><span class="cx">     metaData.headerChecksum = hashData(record.header);
</span><span class="cx">     metaData.headerSize = record.header.size();
</span><del>-    metaData.bodyHash = bodyHash;
</del><ins>+    metaData.bodyHash = blob ? blob.value().hash : computeSHA1(record.body);
</ins><span class="cx">     metaData.bodySize = record.body.size();
</span><ins>+    metaData.isBodyInline = !blob;
</ins><span class="cx"> 
</span><span class="cx">     auto encodedMetaData = encodeRecordMetaData(metaData);
</span><span class="cx">     auto headerData = concatenate(encodedMetaData, record.header);
</span><ins>+
+    if (metaData.isBodyInline)
+        return concatenate(headerData, record.body);
+
</ins><span class="cx">     return { headerData };
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -362,16 +406,16 @@
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="cx">     ASSERT(m_activeReadOperations.contains(&amp;read));
</span><span class="cx"> 
</span><del>-    ioQueue().dispatch([this, &amp;read] {
-        auto recordsPath = this-&gt;recordsPath();
-        auto recordPath = recordPathForKey(read.key, recordsPath);
-        auto bodyPath = bodyPathForKey(read.key, recordsPath);
-        // FIXME: Body and header retrieves can be done in parallel.
-        auto bodyBlob = m_blobStorage.get(bodyPath);
</del><ins>+    auto recordsPath = this-&gt;recordsPath();
+    auto recordPath = recordPathForKey(read.key, recordsPath);
</ins><span class="cx"> 
</span><del>-        RefPtr&lt;IOChannel&gt; channel = IOChannel::open(recordPath, IOChannel::Type::Read);
-        channel-&gt;read(0, std::numeric_limits&lt;size_t&gt;::max(), [this, &amp;read, bodyBlob](Data&amp; fileData, int error) {
-            auto record = error ? nullptr : createRecord(fileData, bodyBlob, read.key);
</del><ins>+    RefPtr&lt;IOChannel&gt; channel = IOChannel::open(recordPath, IOChannel::Type::Read);
+    channel-&gt;read(0, std::numeric_limits&lt;size_t&gt;::max(), &amp;ioQueue(), [this, &amp;read](const Data&amp; fileData, int error) {
+        auto record = error ? nullptr : decodeRecord(fileData, read.key);
+
+        auto* recordPtr = record.release();
+        RunLoop::main().dispatch([this, &amp;read, recordPtr] {
+            auto record = std::unique_ptr&lt;Record&gt;(recordPtr);
</ins><span class="cx">             finishReadOperation(read, WTF::move(record));
</span><span class="cx">         });
</span><span class="cx">     });
</span><span class="lines">@@ -448,6 +492,12 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static bool shouldStoreBodyAsBlob(const Data&amp; bodyData)
+{
+    const size_t maximumInlineBodySize { 16 * 1024 };
+    return bodyData.size() &gt; maximumInlineBodySize;
+}
+
</ins><span class="cx"> void Storage::dispatchWriteOperation(const WriteOperation&amp; write)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(RunLoop::isMain());
</span><span class="lines">@@ -460,36 +510,19 @@
</span><span class="cx">         auto recordsPath = this-&gt;recordsPath();
</span><span class="cx">         auto partitionPath = partitionPathForKey(write.record.key, recordsPath);
</span><span class="cx">         auto recordPath = recordPathForKey(write.record.key, recordsPath);
</span><del>-        auto bodyPath = bodyPathForKey(write.record.key, recordsPath);
</del><span class="cx"> 
</span><span class="cx">         WebCore::makeAllDirectories(partitionPath);
</span><span class="cx"> 
</span><del>-        // Store the body.
-        auto blob = m_blobStorage.add(bodyPath, write.record.body);
-        if (blob.data.isNull()) {
-            RunLoop::main().dispatch([this, &amp;write] {
-                finishWriteOperation(write);
-            });
-            return;
-        }
</del><ins>+        bool shouldStoreAsBlob = shouldStoreBodyAsBlob(write.record.body);
+        auto bodyBlob = shouldStoreAsBlob ? storeBodyAsBlob(write.record, write.mappedBodyHandler) : Nullopt;
</ins><span class="cx"> 
</span><del>-        // Tell the client we now have a disk-backed map for this data.
-        size_t minimumMapSize = pageSize();
-        if (blob.data.size() &gt;= minimumMapSize &amp;&amp; blob.data.isMap() &amp;&amp; write.mappedBodyHandler) {
-            auto&amp; mappedBodyHandler = write.mappedBodyHandler;
-            RunLoop::main().dispatch([blob, mappedBodyHandler] {
-                mappedBodyHandler(blob.data);
-            });
-        }
</del><ins>+        auto recordData = encodeRecord(write.record, bodyBlob);
</ins><span class="cx"> 
</span><del>-        // Store the header and meta data.
-        auto encodedHeader = encodeRecordHeader(write.record, blob.hash);
</del><span class="cx">         auto channel = IOChannel::open(recordPath, IOChannel::Type::Create);
</span><del>-        int fd = channel-&gt;fileDescriptor();
-        size_t headerSize = encodedHeader.size();
-        channel-&gt;write(0, encodedHeader, [this, &amp;write, headerSize, fd](int error) {
</del><ins>+        size_t recordSize = recordData.size();
+        channel-&gt;write(0, recordData, nullptr, [this, &amp;write, recordSize](int error) {
</ins><span class="cx">             // On error the entry still stays in the contents filter until next synchronization.
</span><del>-            m_approximateSize += headerSize;
</del><ins>+            m_approximateSize += recordSize;
</ins><span class="cx">             finishWriteOperation(write);
</span><span class="cx"> 
</span><span class="cx">             LOG(NetworkCacheStorage, &quot;(NetworkProcess) write complete error=%d&quot;, error);
</span><span class="lines">@@ -561,7 +594,7 @@
</span><span class="cx"> 
</span><span class="cx">             auto channel = IOChannel::open(recordPath, IOChannel::Type::Read);
</span><span class="cx">             // FIXME: Traversal is slower than it should be due to lack of parallelism.
</span><del>-            channel-&gt;readSync(0, std::numeric_limits&lt;size_t&gt;::max(), [this, &amp;traverseHandler, &amp;info](Data&amp; fileData, int) {
</del><ins>+            channel-&gt;readSync(0, std::numeric_limits&lt;size_t&gt;::max(), nullptr, [this, &amp;traverseHandler, &amp;info](Data&amp; fileData, int) {
</ins><span class="cx">                 RecordMetaData metaData;
</span><span class="cx">                 Data headerData;
</span><span class="cx">                 if (decodeRecordHeader(fileData, metaData, headerData)) {
</span></span></pre></div>
<a id="trunkSourceWebKit2NetworkProcesscacheNetworkCacheStorageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h (182855 => 182856)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-04-15 19:43:22 UTC (rev 182855)
+++ trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h        2015-04-15 19:54:21 UTC (rev 182856)
</span><span class="lines">@@ -111,6 +111,10 @@
</span><span class="cx">     void dispatchPendingWriteOperations();
</span><span class="cx">     void finishWriteOperation(const WriteOperation&amp;);
</span><span class="cx"> 
</span><ins>+    Optional&lt;BlobStorage::Blob&gt; storeBodyAsBlob(const Record&amp;, const MappedBodyHandler&amp;);
+    Data encodeRecord(const Record&amp;, Optional&lt;BlobStorage::Blob&gt;);
+    std::unique_ptr&lt;Record&gt; decodeRecord(const Data&amp;, const Key&amp;);
+
</ins><span class="cx">     void updateFileModificationTime(const String&amp; path);
</span><span class="cx"> 
</span><span class="cx">     WorkQueue&amp; ioQueue() { return m_ioQueue.get(); }
</span></span></pre>
</div>
</div>

</body>
</html>