Bug 1264642 - Part 2. Add BufferList::Extract and Pickle::ExtractBuffers. r=billm
☠☠ backed out by b25d09b7fab5 ☠ ☠
authorKan-Ru Chen <kanru@kanru.info>
Thu, 21 Jul 2016 17:04:12 +0800
changeset 309694 06fc278fcedf5fa20ec0b569643933cb3fc71983
parent 309693 162098402acc7fd61e011fc817e8e6ce1127ac8c
child 309695 9f434697ef2ecece5b93ad07093b85667d2018e3
push id30569
push userkwierso@gmail.com
push dateWed, 17 Aug 2016 23:34:23 +0000
treeherdermozilla-central@b25d09b7fab5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1264642
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1264642 - Part 2. Add BufferList::Extract and Pickle::ExtractBuffers. r=billm These methods allow us to move some buffers out of a pickle with minimum copying. It's useful when the IPC deserialized type uses BufferList to store data and we want to take the buffers from IPC directly. Borrowing is not suitable to use for IPC to hand out data because we often want to store the data somewhere for processing after IPC has released the underlying buffers. MozReview-Commit-ID: F1K2ZMkACqq
ipc/chromium/src/base/pickle.cc
ipc/chromium/src/base/pickle.h
mfbt/BufferList.h
mfbt/tests/TestBufferList.cpp
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -420,16 +420,31 @@ bool Pickle::FlattenBytes(PickleIterator
     return false;
   }
 
   header_ = reinterpret_cast<Header*>(buffers_.Start());
 
   return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
 }
 
+bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers) const
+{
+  if (AlignInt(length) < length) {
+    return false;
+  }
+
+  bool success;
+  *buffers = const_cast<BufferList*>(&buffers_)->Extract(iter->iter_, length, &success);
+  if (!success) {
+    return false;
+  }
+
+  return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
+}
+
 bool Pickle::ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const {
   if (AlignInt(length) < length) {
     return false;
   }
 
   if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) {
     return false;
   }
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -104,16 +104,17 @@ class Pickle {
   MOZ_MUST_USE bool ReadDouble(PickleIterator* iter, double* result) const;
   MOZ_MUST_USE bool ReadIntPtr(PickleIterator* iter, intptr_t* result) const;
   MOZ_MUST_USE bool ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const;
   MOZ_MUST_USE bool ReadString(PickleIterator* iter, std::string* result) const;
   MOZ_MUST_USE bool ReadWString(PickleIterator* iter, std::wstring* result) const;
   MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const;
   MOZ_MUST_USE bool FlattenBytes(PickleIterator* iter, const char** data, uint32_t length,
                                  uint32_t alignment = sizeof(memberAlignmentType));
+  MOZ_MUST_USE bool ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers) const;
 
   // Safer version of ReadInt() checks for the result not being negative.
   // Use it for reading the object sizes.
   MOZ_MUST_USE bool ReadLength(PickleIterator* iter, int* result) const;
 
   MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const;
 
   void EndRead(PickleIterator& iter) const;
--- a/mfbt/BufferList.h
+++ b/mfbt/BufferList.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_BufferList_h
 #define mozilla_BufferList_h
 
 #include <algorithm>
 #include "mozilla/AllocPolicy.h"
 #include "mozilla/Move.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/Types.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Vector.h"
 #include <string.h>
 
 // BufferList represents a sequence of buffers of data. A BufferList can choose
 // to own its buffers or not. The class handles writing to the buffers,
 // iterating over them, and reading data out. Unlike SegmentedVector, the
@@ -252,16 +253,26 @@ class BufferList : private AllocPolicy
   // aIter. Borrow can fail, in which case *aSuccess will be false upon
   // return. The borrowed BufferList can use a different AllocPolicy than the
   // original one. However, it is not responsible for freeing buffers, so the
   // AllocPolicy is only used for the buffer vector.
   template<typename BorrowingAllocPolicy>
   BufferList<BorrowingAllocPolicy> Borrow(IterImpl& aIter, size_t aSize, bool* aSuccess,
                                           BorrowingAllocPolicy aAP = BorrowingAllocPolicy());
 
+  // Return a new BufferList that adopts the byte range starting at Iter so that
+  // range [aIter, aIter + aSize) is transplanted to the returned BufferList.
+  // Contents of the buffer before aIter + aSize is left undefined.
+  // Extract can fail, in which case *aSuccess will be false upon return. The
+  // moved buffers are erased from the original BufferList. In case of extract
+  // fails, the original BufferList is intact.  All other iterators except aIter
+  // are invalidated.
+  // This method requires aIter and aSize to be 8-byte aligned.
+  BufferList Extract(IterImpl& aIter, size_t aSize, bool* aSuccess);
+
 private:
   explicit BufferList(AllocPolicy aAP)
    : AllocPolicy(aAP),
      mOwning(false),
      mSize(0),
      mStandardCapacity(0)
   {
   }
@@ -425,11 +436,79 @@ BufferList<AllocPolicy>::Borrow(IterImpl
     size -= toAdvance;
   }
 
   result.mSize = aSize;
   *aSuccess = true;
   return result;
 }
 
+template<typename AllocPolicy>
+BufferList<AllocPolicy>
+BufferList<AllocPolicy>::Extract(IterImpl& aIter, size_t aSize, bool* aSuccess)
+{
+  MOZ_RELEASE_ASSERT(aSize);
+  MOZ_RELEASE_ASSERT(mOwning);
+  MOZ_ASSERT(aSize % kSegmentAlignment == 0);
+  MOZ_ASSERT(intptr_t(aIter.mData) % kSegmentAlignment == 0);
+
+  IterImpl iter = aIter;
+  size_t size = aSize;
+  size_t toCopy = std::min(size, aIter.RemainingInSegment());
+  MOZ_ASSERT(toCopy % kSegmentAlignment == 0);
+
+  BufferList result(0, toCopy, mStandardCapacity);
+  BufferList error(0, 0, mStandardCapacity);
+
+  // Copy the head
+  if (!result.WriteBytes(aIter.mData, toCopy)) {
+    *aSuccess = false;
+    return error;
+  }
+  iter.Advance(*this, toCopy);
+  size -= toCopy;
+
+  // Move segments to result
+  auto resultGuard = MakeScopeExit([&] {
+    *aSuccess = false;
+    result.mSegments.erase(result.mSegments.begin()+1, result.mSegments.end());
+  });
+
+  size_t movedSize = 0;
+  uintptr_t toRemoveStart = iter.mSegment;
+  uintptr_t toRemoveEnd = iter.mSegment;
+  while (!iter.Done() &&
+         !iter.HasRoomFor(size)) {
+    if (!result.mSegments.append(Segment(mSegments[iter.mSegment].mData,
+                                         mSegments[iter.mSegment].mSize,
+                                         mSegments[iter.mSegment].mCapacity))) {
+      return error;
+    }
+    movedSize += iter.RemainingInSegment();
+    size -= iter.RemainingInSegment();
+    toRemoveEnd++;
+    iter.Advance(*this, iter.RemainingInSegment());
+  }
+
+  if (size)  {
+    if (!iter.HasRoomFor(size) ||
+        !result.WriteBytes(iter.Data(), size)) {
+      return error;
+    }
+    iter.Advance(*this, size);
+  }
+
+  mSegments.erase(mSegments.begin() + toRemoveStart, mSegments.begin() + toRemoveEnd);
+  mSize -= movedSize;
+  aIter.mSegment = iter.mSegment - (toRemoveEnd - toRemoveStart);
+  aIter.mData = iter.mData;
+  aIter.mDataEnd = iter.mDataEnd;
+  MOZ_ASSERT(aIter.mDataEnd == mSegments[aIter.mSegment].End());
+  result.mSize = aSize;
+
+  resultGuard.release();
+  *aSuccess = true;
+  return result;
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_BufferList_h */
--- a/mfbt/tests/TestBufferList.cpp
+++ b/mfbt/tests/TestBufferList.cpp
@@ -234,10 +234,32 @@ int main(void)
 
   BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter());
   iter1.Advance(bl, kBorrowStart);
   MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
   MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5));
   MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5));
   MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
 
+  // Extracting.
+
+  const size_t kExtractStart = 8;
+  const size_t kExtractSize = 24;
+  const size_t kExtractOverSize = 1000;
+
+  iter = bl.Iter();
+  iter.Advance(bl, kExtractStart);
+  bl2 = bl.Extract(iter, kExtractSize, &success);
+  MOZ_RELEASE_ASSERT(success);
+  MOZ_RELEASE_ASSERT(bl2.Size() == kExtractSize);
+
+  BufferList bl3 = bl.Extract(iter, kExtractOverSize, &success);
+  MOZ_RELEASE_ASSERT(!success);
+
+  MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kExtractSize - kExtractStart));
+  MOZ_RELEASE_ASSERT(iter.Done());
+
+  iter = bl2.Iter();
+  MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kExtractSize));
+  MOZ_RELEASE_ASSERT(iter.Done());
+
   return 0;
 }