Bug 1264642 - Part 3. Add BufferList::MoveFallible. r=billm draft
authorKan-Ru Chen <kanru@kanru.info>
Thu, 14 Jul 2016 16:27:29 +0800
changeset 403873 6637ec04c68e951a5917063dc82e7f585d0049b4
parent 403872 d24e0497407e48b031a7a7f1cb2861ae927342ff
child 403874 24d3b36b20213772878f1490f78fba1344fc5a7f
child 404022 bac19b6e82e95f9e7b12017356a8cc8460f7a998
push id27038
push userbmo:kchen@mozilla.com
push dateMon, 22 Aug 2016 11:42:11 +0000
reviewersbillm
bugs1264642
milestone51.0a1
Bug 1264642 - Part 3. Add BufferList::MoveFallible. r=billm It works like a move constructor but it's fallible. It can also move data to different but compatible AllocPolicy. MozReview-Commit-ID: LAbPWCwnrr6
mfbt/BufferList.h
mfbt/tests/TestBufferList.cpp
--- a/mfbt/BufferList.h
+++ b/mfbt/BufferList.h
@@ -47,16 +47,19 @@ class BufferList : private AllocPolicy
 
     Segment(Segment&&) = default;
     Segment& operator=(Segment&&) = default;
 
     char* Start() const { return mData; }
     char* End() const { return mData + mSize; }
   };
 
+  template<typename OtherAllocPolicy>
+  friend class BufferList;
+
  public:
   // For the convenience of callers, all segments are required to be a multiple
   // of 8 bytes in capacity. Also, every buffer except the last one is required
   // to be full (i.e., size == capacity). Therefore, a byte at offset N within
   // the BufferList and stored in memory at an address A will satisfy
   // (N % Align == A % Align) if Align == 2, 4, or 8.
   //
   // NB: FlattenBytes can create non-full segments in the middle of the
@@ -253,16 +256,25 @@ 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 and move storage from this BufferList to it. The
+  // new BufferList owns the buffers. Move can fail, in which case *aSuccess
+  // will be false upon return. The new BufferList can use a different
+  // AllocPolicy than the original one. The new OtherAllocPolicy is responsible
+  // for freeing buffers, so the OtherAllocPolicy must use freeing method
+  // compatible to the original one.
+  template<typename OtherAllocPolicy>
+  BufferList<OtherAllocPolicy> MoveFallible(bool* aSuccess, OtherAllocPolicy aAP = OtherAllocPolicy());
+
   // 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.
@@ -423,29 +435,53 @@ BufferList<AllocPolicy>::Borrow(IterImpl
                                 BorrowingAllocPolicy aAP)
 {
   BufferList<BorrowingAllocPolicy> result(aAP);
 
   size_t size = aSize;
   while (size) {
     size_t toAdvance = std::min(size, aIter.RemainingInSegment());
 
-    if (!toAdvance || !result.mSegments.append(Segment(aIter.mData, toAdvance, toAdvance))) {
+    if (!toAdvance || !result.mSegments.append(typename BufferList<BorrowingAllocPolicy>::Segment(aIter.mData, toAdvance, toAdvance))) {
       *aSuccess = false;
       return result;
     }
     aIter.Advance(*this, toAdvance);
     size -= toAdvance;
   }
 
   result.mSize = aSize;
   *aSuccess = true;
   return result;
 }
 
+template<typename AllocPolicy> template<typename OtherAllocPolicy>
+BufferList<OtherAllocPolicy>
+BufferList<AllocPolicy>::MoveFallible(bool* aSuccess, OtherAllocPolicy aAP)
+{
+  BufferList<OtherAllocPolicy> result(0, 0, mStandardCapacity, aAP);
+
+  IterImpl iter = Iter();
+  while (!iter.Done()) {
+    size_t toAdvance = iter.RemainingInSegment();
+
+    if (!toAdvance || !result.mSegments.append(typename BufferList<OtherAllocPolicy>::Segment(iter.mData, toAdvance, toAdvance))) {
+      *aSuccess = false;
+      return result;
+    }
+    iter.Advance(*this, toAdvance);
+  }
+
+  result.mSize = mSize;
+  mSegments.clear();
+  mSize = 0;
+  *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);
--- a/mfbt/tests/TestBufferList.cpp
+++ b/mfbt/tests/TestBufferList.cpp
@@ -208,24 +208,38 @@ int main(void)
   bl = mozilla::Move(bl2);
   MOZ_RELEASE_ASSERT(bl2.Size() == 0);
   MOZ_RELEASE_ASSERT(bl2.Iter().Done());
 
   iter = bl.Iter();
   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3));
   MOZ_RELEASE_ASSERT(iter.Done());
 
+  // MoveFallible
+
+  bool success;
+  bl2 = bl.MoveFallible<InfallibleAllocPolicy>(&success);
+  MOZ_RELEASE_ASSERT(success);
+  MOZ_RELEASE_ASSERT(bl.Size() == 0);
+  MOZ_RELEASE_ASSERT(bl.Iter().Done());
+  MOZ_RELEASE_ASSERT(bl2.Size() == kSmallWrite * 3);
+
+  iter = bl2.Iter();
+  MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kSmallWrite * 3));
+  MOZ_RELEASE_ASSERT(iter.Done());
+
+  bl = bl2.MoveFallible<InfallibleAllocPolicy>(&success);
+
   // Borrowing.
 
   const size_t kBorrowStart = 4;
   const size_t kBorrowSize = 24;
 
   iter = bl.Iter();
   iter.Advance(bl, kBorrowStart);
-  bool success;
   bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success);
   MOZ_RELEASE_ASSERT(success);
   MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize);
 
   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart));
   MOZ_RELEASE_ASSERT(iter.Done());
 
   iter = bl2.Iter();