LCOV - code coverage report
Current view: top level - image - SourceBuffer.h (source / functions) Hit Total Coverage
Test: output.info Lines: 70 87 80.5 %
Date: 2017-07-14 16:53:18 Functions: 21 26 80.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /**
       7             :  * SourceBuffer is a single producer, multiple consumer data structure used for
       8             :  * storing image source (compressed) data.
       9             :  */
      10             : 
      11             : #ifndef mozilla_image_sourcebuffer_h
      12             : #define mozilla_image_sourcebuffer_h
      13             : 
      14             : #include <algorithm>
      15             : #include "mozilla/Maybe.h"
      16             : #include "mozilla/MemoryReporting.h"
      17             : #include "mozilla/Mutex.h"
      18             : #include "mozilla/Move.h"
      19             : #include "mozilla/MemoryReporting.h"
      20             : #include "mozilla/RefPtr.h"
      21             : #include "mozilla/RefCounted.h"
      22             : #include "mozilla/UniquePtr.h"
      23             : #include "mozilla/RefPtr.h"
      24             : #include "nsTArray.h"
      25             : 
      26             : class nsIInputStream;
      27             : 
      28             : namespace mozilla {
      29             : namespace image {
      30             : 
      31             : class SourceBuffer;
      32             : 
      33             : /**
      34             :  * IResumable is an interface for classes that can schedule themselves to resume
      35             :  * their work later. An implementation of IResumable generally should post a
      36             :  * runnable to some event target which continues the work of the task.
      37             :  */
      38          33 : struct IResumable
      39             : {
      40             :   MOZ_DECLARE_REFCOUNTED_TYPENAME(IResumable)
      41             : 
      42             :   // Subclasses may or may not be XPCOM classes, so we just require that they
      43             :   // implement AddRef and Release.
      44             :   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
      45             : 
      46             :   virtual void Resume() = 0;
      47             : 
      48             : protected:
      49          19 :   virtual ~IResumable() { }
      50             : };
      51             : 
      52             : /**
      53             :  * SourceBufferIterator is a class that allows consumers of image source data to
      54             :  * read the contents of a SourceBuffer sequentially.
      55             :  *
      56             :  * Consumers can advance through the SourceBuffer by calling
      57             :  * AdvanceOrScheduleResume() repeatedly. After every advance, they should call
      58             :  * check the return value, which will tell them the iterator's new state.
      59             :  *
      60             :  * If WAITING is returned, AdvanceOrScheduleResume() has arranged
      61             :  * to call the consumer's Resume() method later, so the consumer should save its
      62             :  * state if needed and stop running.
      63             :  *
      64             :  * If the iterator's new state is READY, then the consumer can call Data() and
      65             :  * Length() to read new data from the SourceBuffer.
      66             :  *
      67             :  * Finally, in the COMPLETE state the consumer can call CompletionStatus() to
      68             :  * get the status passed to SourceBuffer::Complete().
      69             :  */
      70             : class SourceBufferIterator final
      71             : {
      72             : public:
      73             :   enum State {
      74             :     START,    // The iterator is at the beginning of the buffer.
      75             :     READY,    // The iterator is pointing to new data.
      76             :     WAITING,  // The iterator is blocked and the caller must yield.
      77             :     COMPLETE  // The iterator is pointing to the end of the buffer.
      78             :   };
      79             : 
      80          33 :   explicit SourceBufferIterator(SourceBuffer* aOwner)
      81          33 :     : mOwner(aOwner)
      82             :     , mState(START)
      83             :     , mChunkCount(0)
      84          33 :     , mByteCount(0)
      85             :   {
      86          33 :     MOZ_ASSERT(aOwner);
      87          33 :     mData.mIterating.mChunk = 0;
      88          33 :     mData.mIterating.mData = nullptr;
      89          33 :     mData.mIterating.mOffset = 0;
      90          33 :     mData.mIterating.mAvailableLength = 0;
      91          33 :     mData.mIterating.mNextReadLength = 0;
      92          33 :   }
      93             : 
      94          33 :   SourceBufferIterator(SourceBufferIterator&& aOther)
      95          33 :     : mOwner(Move(aOther.mOwner))
      96          33 :     , mState(aOther.mState)
      97             :     , mData(aOther.mData)
      98          33 :     , mChunkCount(aOther.mChunkCount)
      99          99 :     , mByteCount(aOther.mByteCount)
     100          33 :   { }
     101             : 
     102             :   ~SourceBufferIterator();
     103             : 
     104             :   SourceBufferIterator& operator=(SourceBufferIterator&& aOther);
     105             : 
     106             :   /**
     107             :    * Returns true if there are no more than @aBytes remaining in the
     108             :    * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
     109             :    */
     110             :   bool RemainingBytesIsNoMoreThan(size_t aBytes) const;
     111             : 
     112             :   /**
     113             :    * Advances the iterator through the SourceBuffer if possible. Advances no
     114             :    * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
     115             :    * possible.)
     116             :    *
     117             :    * This is a wrapper around AdvanceOrScheduleResume() that makes it clearer at
     118             :    * the callsite when the no resuming is intended.
     119             :    *
     120             :    * @return State::READY if the iterator was successfully advanced.
     121             :    *         State::WAITING if the iterator could not be advanced because it's
     122             :    *           at the end of the underlying SourceBuffer, but the SourceBuffer
     123             :    *           may still receive additional data.
     124             :    *         State::COMPLETE if the iterator could not be advanced because it's
     125             :    *           at the end of the underlying SourceBuffer and the SourceBuffer is
     126             :    *           marked complete (i.e., it will never receive any additional
     127             :    *           data).
     128             :    */
     129             :   State Advance(size_t aRequestedBytes)
     130             :   {
     131             :     return AdvanceOrScheduleResume(aRequestedBytes, nullptr);
     132             :   }
     133             : 
     134             :   /**
     135             :    * Advances the iterator through the SourceBuffer if possible. Advances no
     136             :    * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
     137             :    * possible.) If advancing is not possible and @aConsumer is not null,
     138             :    * arranges to call the @aConsumer's Resume() method when more data is
     139             :    * available.
     140             :    *
     141             :    * @return State::READY if the iterator was successfully advanced.
     142             :    *         State::WAITING if the iterator could not be advanced because it's
     143             :    *           at the end of the underlying SourceBuffer, but the SourceBuffer
     144             :    *           may still receive additional data. @aConsumer's Resume() method
     145             :    *           will be called when additional data is available.
     146             :    *         State::COMPLETE if the iterator could not be advanced because it's
     147             :    *           at the end of the underlying SourceBuffer and the SourceBuffer is
     148             :    *           marked complete (i.e., it will never receive any additional
     149             :    *           data).
     150             :    */
     151             :   State AdvanceOrScheduleResume(size_t aRequestedBytes, IResumable* aConsumer);
     152             : 
     153             :   /// If at the end, returns the status passed to SourceBuffer::Complete().
     154           0 :   nsresult CompletionStatus() const
     155             :   {
     156           0 :     MOZ_ASSERT(mState == COMPLETE,
     157             :                "Calling CompletionStatus() in the wrong state");
     158           0 :     return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;
     159             :   }
     160             : 
     161             :   /// If we're ready to read, returns a pointer to the new data.
     162         145 :   const char* Data() const
     163             :   {
     164         145 :     MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
     165         145 :     return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
     166         145 :                            : nullptr;
     167             :   }
     168             : 
     169             :   /// If we're ready to read, returns the length of the new data.
     170         315 :   size_t Length() const
     171             :   {
     172         315 :     MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
     173         315 :     return mState == READY ? mData.mIterating.mNextReadLength : 0;
     174             :   }
     175             : 
     176             :   /// @return a count of the chunks we've advanced through.
     177          33 :   uint32_t ChunkCount() const { return mChunkCount; }
     178             : 
     179             :   /// @return a count of the bytes in all chunks we've advanced through.
     180          33 :   size_t ByteCount() const { return mByteCount; }
     181             : 
     182             : private:
     183             :   friend class SourceBuffer;
     184             : 
     185             :   SourceBufferIterator(const SourceBufferIterator&) = delete;
     186             :   SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
     187             : 
     188          93 :   bool HasMore() const { return mState != COMPLETE; }
     189             : 
     190          54 :   State AdvanceFromLocalBuffer(size_t aRequestedBytes)
     191             :   {
     192          54 :     MOZ_ASSERT(mState == READY, "Advancing in the wrong state");
     193          54 :     MOZ_ASSERT(mData.mIterating.mAvailableLength > 0,
     194             :                "The local buffer shouldn't be empty");
     195          54 :     MOZ_ASSERT(mData.mIterating.mNextReadLength == 0,
     196             :                "Advancing without consuming previous data");
     197             : 
     198          54 :     mData.mIterating.mNextReadLength =
     199          54 :       std::min(mData.mIterating.mAvailableLength, aRequestedBytes);
     200             : 
     201          54 :     return READY;
     202             :   }
     203             : 
     204          33 :   State SetReady(uint32_t aChunk, const char* aData,
     205             :                  size_t aOffset, size_t aAvailableLength,
     206             :                  size_t aRequestedBytes)
     207             :   {
     208          33 :     MOZ_ASSERT(mState != COMPLETE);
     209          33 :     mState = READY;
     210             : 
     211             :     // Update state.
     212          33 :     mData.mIterating.mChunk = aChunk;
     213          33 :     mData.mIterating.mData = aData;
     214          33 :     mData.mIterating.mOffset = aOffset;
     215          33 :     mData.mIterating.mAvailableLength = aAvailableLength;
     216             : 
     217             :     // Update metrics.
     218          33 :     mChunkCount++;
     219          33 :     mByteCount += aAvailableLength;
     220             : 
     221             :     // Attempt to advance by the requested number of bytes.
     222          33 :     return AdvanceFromLocalBuffer(aRequestedBytes);
     223             :   }
     224             : 
     225           0 :   State SetWaiting()
     226             :   {
     227           0 :     MOZ_ASSERT(mState != COMPLETE);
     228           0 :     MOZ_ASSERT(mState != WAITING, "Did we get a spurious wakeup somehow?");
     229           0 :     return mState = WAITING;
     230             :   }
     231             : 
     232           0 :   State SetComplete(nsresult aStatus)
     233             :   {
     234           0 :     mData.mAtEnd.mStatus = aStatus;
     235           0 :     return mState = COMPLETE;
     236             :   }
     237             : 
     238             :   RefPtr<SourceBuffer> mOwner;
     239             : 
     240             :   State mState;
     241             : 
     242             :   /**
     243             :    * This union contains our iteration state if we're still iterating (for
     244             :    * states START, READY, and WAITING) and the status the SourceBuffer was
     245             :    * completed with if we're in state COMPLETE.
     246             :    */
     247             :   union {
     248             :     struct {
     249             :       uint32_t mChunk;
     250             :       const char* mData;
     251             :       size_t mOffset;
     252             :       size_t mAvailableLength;
     253             :       size_t mNextReadLength;
     254             :     } mIterating;
     255             :     struct {
     256             :       nsresult mStatus;
     257             :     } mAtEnd;
     258             :   } mData;
     259             : 
     260             :   uint32_t mChunkCount;  // Count of chunks we've advanced through.
     261             :   size_t mByteCount;     // Count of bytes in all chunks we've advanced through.
     262             : };
     263             : 
     264             : /**
     265             :  * SourceBuffer is a parallel data structure used for storing image source
     266             :  * (compressed) data.
     267             :  *
     268             :  * SourceBuffer is a single producer, multiple consumer data structure. The
     269             :  * single producer calls Append() to append data to the buffer. In parallel,
     270             :  * multiple consumers can call Iterator(), which returns a SourceBufferIterator
     271             :  * that they can use to iterate through the buffer. The SourceBufferIterator
     272             :  * returns a series of pointers which remain stable for lifetime of the
     273             :  * SourceBuffer, and the data they point to is immutable, ensuring that the
     274             :  * producer never interferes with the consumers.
     275             :  *
     276             :  * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to
     277             :  * keep a list of consumers which are waiting for new data, and to resume them
     278             :  * when the producer appends more. All consumers must implement the IResumable
     279             :  * interface to make this possible.
     280             :  */
     281             : class SourceBuffer final
     282             : {
     283             : public:
     284             :   MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
     285          88 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
     286             : 
     287             :   SourceBuffer();
     288             : 
     289             :   //////////////////////////////////////////////////////////////////////////////
     290             :   // Producer methods.
     291             :   //////////////////////////////////////////////////////////////////////////////
     292             : 
     293             :   /**
     294             :    * If the producer knows how long the source data will be, it should call
     295             :    * ExpectLength, which enables SourceBuffer to preallocate its buffer.
     296             :    */
     297             :   nsresult ExpectLength(size_t aExpectedLength);
     298             : 
     299             :   /// Append the provided data to the buffer.
     300             :   nsresult Append(const char* aData, size_t aLength);
     301             : 
     302             :   /// Append the data available on the provided nsIInputStream to the buffer.
     303             :   nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount);
     304             : 
     305             :   /**
     306             :    * Mark the buffer complete, with a status that will be available to
     307             :    * consumers. Further calls to Append() are forbidden after Complete().
     308             :    */
     309             :   void Complete(nsresult aStatus);
     310             : 
     311             :   /// Returns true if the buffer is complete.
     312             :   bool IsComplete();
     313             : 
     314             :   /// Memory reporting.
     315             :   size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
     316             : 
     317             : 
     318             :   //////////////////////////////////////////////////////////////////////////////
     319             :   // Consumer methods.
     320             :   //////////////////////////////////////////////////////////////////////////////
     321             : 
     322             :   /// Returns an iterator to this SourceBuffer.
     323             :   SourceBufferIterator Iterator();
     324             : 
     325             : 
     326             :   //////////////////////////////////////////////////////////////////////////////
     327             :   // Consumer methods.
     328             :   //////////////////////////////////////////////////////////////////////////////
     329             : 
     330             :   /**
     331             :    * The minimum chunk capacity we'll allocate, if we don't know the correct
     332             :    * capacity (which would happen because ExpectLength() wasn't called or gave
     333             :    * us the wrong value). This is only exposed for use by tests; if normal code
     334             :    * is using this, it's doing something wrong.
     335             :    */
     336             :   static const size_t MIN_CHUNK_CAPACITY = 4096;
     337             : 
     338             : private:
     339             :   friend class SourceBufferIterator;
     340             : 
     341             :   ~SourceBuffer();
     342             : 
     343             :   //////////////////////////////////////////////////////////////////////////////
     344             :   // Chunk type and chunk-related methods.
     345             :   //////////////////////////////////////////////////////////////////////////////
     346             : 
     347          95 :   class Chunk
     348             :   {
     349             :   public:
     350          38 :     explicit Chunk(size_t aCapacity)
     351          38 :       : mCapacity(aCapacity)
     352          38 :       , mLength(0)
     353             :     {
     354          38 :       MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
     355          76 :       mData.reset(new (fallible) char[mCapacity]);
     356          38 :     }
     357             : 
     358          76 :     Chunk(Chunk&& aOther)
     359          76 :       : mCapacity(aOther.mCapacity)
     360          76 :       , mLength(aOther.mLength)
     361         152 :       , mData(Move(aOther.mData))
     362             :     {
     363          76 :       aOther.mCapacity = aOther.mLength = 0;
     364          76 :       aOther.mData = nullptr;
     365          76 :     }
     366             : 
     367           0 :     Chunk& operator=(Chunk&& aOther)
     368             :     {
     369           0 :       mCapacity = aOther.mCapacity;
     370           0 :       mLength = aOther.mLength;
     371           0 :       mData = Move(aOther.mData);
     372           0 :       aOther.mCapacity = aOther.mLength = 0;
     373           0 :       aOther.mData = nullptr;
     374           0 :       return *this;
     375             :     }
     376             : 
     377          57 :     bool AllocationFailed() const { return !mData; }
     378         143 :     size_t Capacity() const { return mCapacity; }
     379         308 :     size_t Length() const { return mLength; }
     380             : 
     381         130 :     char* Data() const
     382             :     {
     383         130 :       MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
     384         130 :       return mData.get();
     385             :     }
     386             : 
     387          39 :     void AddLength(size_t aAdditionalLength)
     388             :     {
     389          39 :       MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
     390          39 :       mLength += aAdditionalLength;
     391          39 :     }
     392             : 
     393             :   private:
     394             :     Chunk(const Chunk&) = delete;
     395             :     Chunk& operator=(const Chunk&) = delete;
     396             : 
     397             :     size_t mCapacity;
     398             :     size_t mLength;
     399             :     UniquePtr<char[]> mData;
     400             :   };
     401             : 
     402             :   nsresult AppendChunk(Maybe<Chunk>&& aChunk);
     403             :   Maybe<Chunk> CreateChunk(size_t aCapacity, bool aRoundUp = true);
     404             :   nsresult Compact();
     405             :   static size_t RoundedUpCapacity(size_t aCapacity);
     406             :   size_t FibonacciCapacityWithMinimum(size_t aMinCapacity);
     407             : 
     408             : 
     409             :   //////////////////////////////////////////////////////////////////////////////
     410             :   // Iterator / consumer methods.
     411             :   //////////////////////////////////////////////////////////////////////////////
     412             : 
     413             :   void AddWaitingConsumer(IResumable* aConsumer);
     414             :   void ResumeWaitingConsumers();
     415             : 
     416             :   typedef SourceBufferIterator::State State;
     417             : 
     418             :   State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
     419             :                                         size_t aRequestedBytes,
     420             :                                         IResumable* aConsumer);
     421             :   bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
     422             :                                   size_t aBytes) const;
     423             : 
     424             :   void OnIteratorRelease();
     425             : 
     426             :   //////////////////////////////////////////////////////////////////////////////
     427             :   // Helper methods.
     428             :   //////////////////////////////////////////////////////////////////////////////
     429             : 
     430             :   nsresult HandleError(nsresult aError);
     431             :   bool IsEmpty();
     432             :   bool IsLastChunk(uint32_t aChunk);
     433             : 
     434             : 
     435             :   //////////////////////////////////////////////////////////////////////////////
     436             :   // Member variables.
     437             :   //////////////////////////////////////////////////////////////////////////////
     438             : 
     439             :   /// All private members are protected by mMutex.
     440             :   mutable Mutex mMutex;
     441             : 
     442             :   /// The data in this SourceBuffer, stored as a series of Chunks.
     443             :   FallibleTArray<Chunk> mChunks;
     444             : 
     445             :   /// Consumers which are waiting to be notified when new data is available.
     446             :   nsTArray<RefPtr<IResumable>> mWaitingConsumers;
     447             : 
     448             :   /// If present, marks this SourceBuffer complete with the given final status.
     449             :   Maybe<nsresult> mStatus;
     450             : 
     451             :   /// Count of active consumers.
     452             :   uint32_t mConsumerCount;
     453             : };
     454             : 
     455             : } // namespace image
     456             : } // namespace mozilla
     457             : 
     458             : #endif // mozilla_image_sourcebuffer_h

Generated by: LCOV version 1.13