Bug 1373579: Part 2 - Deserialize StructuredCloneBlob in segments rather than a single large allocation. r?billm draft
authorKris Maglione <maglione.k@gmail.com>
Mon, 19 Jun 2017 16:51:34 -0700
changeset 596995 8ef61c6d45e66104c1ddcbc15847e3249c0e7bdf
parent 596994 1b51f7aa00f12c7dcceb758d2b1f07fa7cbd9a4a
child 596996 323845c20f255dee6c9f7850e261ce169548824e
push id64791
push usermaglione.k@gmail.com
push dateMon, 19 Jun 2017 23:58:29 +0000
reviewersbillm
bugs1373579
milestone56.0a1
Bug 1373579: Part 2 - Deserialize StructuredCloneBlob in segments rather than a single large allocation. r?billm MozReview-Commit-ID: QZqPKSTheG
dom/base/StructuredCloneBlob.cpp
mfbt/BufferList.h
--- a/dom/base/StructuredCloneBlob.cpp
+++ b/dom/base/StructuredCloneBlob.cpp
@@ -128,19 +128,24 @@ StructuredCloneBlob::ReadStructuredClone
   uint32_t blobCount;
   if (!JS_ReadUint32Pair(aReader, &blobOffset, &blobCount)) {
     return false;
   }
   if (blobCount) {
     BlobImpls().AppendElements(&aHolder->BlobImpls()[blobOffset], blobCount);
   }
 
-  JSStructuredCloneData data(length, length, 4096);
-  if (!JS_ReadBytes(aReader, data.Start(), length)) {
-    return false;
+  JSStructuredCloneData data;
+  size_t size = 0;
+  while (length) {
+    char* buffer = data.AllocateBytes(length, &size);
+    if (!buffer || !JS_ReadBytes(aReader, buffer, size)) {
+      return false;
+    }
+    length -= size;
   }
 
   mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope,
                                                     &StructuredCloneHolder::sCallbacks,
                                                     this);
   mBuffer->adopt(Move(data), version, &StructuredCloneHolder::sCallbacks);
 
   return true;
--- a/mfbt/BufferList.h
+++ b/mfbt/BufferList.h
@@ -258,16 +258,20 @@ class BufferList : private AllocPolicy
   const char* Start() const { return mSegments[0].mData; }
 
   IterImpl Iter() const { return IterImpl(*this); }
 
   // Copies aSize bytes from aData into the BufferList. The storage for these
   // bytes may be split across multiple buffers. Size() is increased by aSize.
   inline bool WriteBytes(const char* aData, size_t aSize);
 
+  // Allocates a buffer of at most |aMaxBytes| bytes and, if successful, returns
+  // that buffer, and places its size in |aSize|.
+  inline char* AllocateBytes(size_t aMaxSize, size_t* aSize);
+
   // Copies possibly non-contiguous byte range starting at aIter into
   // aData. aIter is advanced by aSize bytes. Returns false if it runs out of
   // data before aSize.
   inline bool ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const;
 
   // Return a new BufferList that shares storage with this BufferList. The new
   // BufferList is read-only. It allows iteration over aSize bytes starting at
   // aIter. Borrow can fail, in which case *aSuccess will be false upon
@@ -308,17 +312,17 @@ private:
   explicit BufferList(AllocPolicy aAP)
    : AllocPolicy(aAP),
      mOwning(false),
      mSize(0),
      mStandardCapacity(0)
   {
   }
 
-  void* AllocateSegment(size_t aSize, size_t aCapacity)
+  char* AllocateSegment(size_t aSize, size_t aCapacity)
   {
     MOZ_RELEASE_ASSERT(mOwning);
     MOZ_ASSERT(aSize <= aCapacity);
 
     char* data = this->template pod_malloc<char>(aCapacity);
     if (!data) {
       return nullptr;
     }
@@ -339,44 +343,58 @@ private:
 template<typename AllocPolicy>
 bool
 BufferList<AllocPolicy>::WriteBytes(const char* aData, size_t aSize)
 {
   MOZ_RELEASE_ASSERT(mOwning);
   MOZ_RELEASE_ASSERT(mStandardCapacity);
 
   size_t copied = 0;
-  size_t remaining = aSize;
+  size_t toCopy = 0;
+  while (copied < aSize) {
+    char* data = AllocateBytes(aSize - copied, &toCopy);
+    if (!data) {
+      return false;
+    }
+    memcpy(data, aData + copied, toCopy);
+    copied += toCopy;
+  }
+
+  return true;
+}
+
+template<typename AllocPolicy>
+char*
+BufferList<AllocPolicy>::AllocateBytes(size_t aMaxSize, size_t* aSize)
+{
+  MOZ_RELEASE_ASSERT(mOwning);
+  MOZ_RELEASE_ASSERT(mStandardCapacity);
 
   if (!mSegments.empty()) {
     Segment& lastSegment = mSegments.back();
 
-    size_t toCopy = std::min(aSize, lastSegment.mCapacity - lastSegment.mSize);
-    memcpy(lastSegment.mData + lastSegment.mSize, aData, toCopy);
-    lastSegment.mSize += toCopy;
-    mSize += toCopy;
+    size_t capacity = lastSegment.mCapacity - lastSegment.mSize;
+    if (capacity) {
+      size_t size = std::min(aMaxSize, capacity);
+      char* data = lastSegment.mData + lastSegment.mSize;
 
-    copied += toCopy;
-    remaining -= toCopy;
+      lastSegment.mSize += size;
+      mSize += size;
+
+      *aSize = size;
+      return data;
+    }
   }
 
-  while (remaining) {
-    size_t toCopy = std::min(remaining, mStandardCapacity);
-
-    void* data = AllocateSegment(toCopy, mStandardCapacity);
-    if (!data) {
-      return false;
-    }
-    memcpy(data, aData + copied, toCopy);
-
-    copied += toCopy;
-    remaining -= toCopy;
+  size_t size = std::min(aMaxSize, mStandardCapacity);
+  char* data = AllocateSegment(size, mStandardCapacity);
+  if (data) {
+    *aSize = size;
   }
-
-  return true;
+  return data;
 }
 
 template<typename AllocPolicy>
 bool
 BufferList<AllocPolicy>::ReadBytes(IterImpl& aIter, char* aData, size_t aSize) const
 {
   size_t copied = 0;
   size_t remaining = aSize;