author Christoph Kerschbaumer <>
Thu, 14 Jan 2016 13:21:31 -0800
changeset 280094 febf0e69c996e1a6678e6d7877e6ef07e0011b2c
parent 269872 912688ff102a86671bb7318e562f82f3519e4c93
child 305171 9b650c8855c2a64e6194680eee53c53b113e4798
permissions -rw-r--r--
Bug 1224694 - Unify and clean up initialization of CSP (r=sicking)

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at */

 * SourceBuffer is a single producer, multiple consumer data structure used for
 * storing image source (compressed) data.

#ifndef mozilla_image_sourcebuffer_h
#define mozilla_image_sourcebuffer_h

#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/Move.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RefPtr.h"
#include "mozilla/RefCounted.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/RefPtr.h"
#include "nsTArray.h"

class nsIInputStream;

namespace mozilla {
namespace image {

class SourceBuffer;

 * IResumable is an interface for classes that can schedule themselves to resume
 * their work later. An implementation of IResumable generally should post a
 * runnable to some event target which continues the work of the task.
struct IResumable

  // Subclasses may or may not be XPCOM classes, so we just require that they
  // implement AddRef and Release.
  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;

  virtual void Resume() = 0;

  virtual ~IResumable() { }

 * SourceBufferIterator is a class that allows consumers of image source data to
 * read the contents of a SourceBuffer sequentially.
 * Consumers can advance through the SourceBuffer by calling
 * AdvanceOrScheduleResume() repeatedly. After every advance, they should call
 * check the return value, which will tell them the iterator's new state.
 * If WAITING is returned, AdvanceOrScheduleResume() has arranged
 * to call the consumer's Resume() method later, so the consumer should save its
 * state if needed and stop running.
 * If the iterator's new state is READY, then the consumer can call Data() and
 * Length() to read new data from the SourceBuffer.
 * Finally, in the COMPLETE state the consumer can call CompletionStatus() to
 * get the status passed to SourceBuffer::Complete().
class SourceBufferIterator final
  enum State {
    START,    // The iterator is at the beginning of the buffer.
    READY,    // The iterator is pointing to new data.
    WAITING,  // The iterator is blocked and the caller must yield.
    COMPLETE  // The iterator is pointing to the end of the buffer.

  explicit SourceBufferIterator(SourceBuffer* aOwner)
    : mOwner(aOwner)
    , mState(START)
    mData.mIterating.mChunk = 0;
    mData.mIterating.mData = nullptr;
    mData.mIterating.mOffset = 0;
    mData.mIterating.mLength = 0;

  SourceBufferIterator(SourceBufferIterator&& aOther)
    : mOwner(Move(aOther.mOwner))
    , mState(aOther.mState)
    , mData(aOther.mData)
  { }


  SourceBufferIterator& operator=(SourceBufferIterator&& aOther)
    mOwner = Move(aOther.mOwner);
    mState = aOther.mState;
    mData = aOther.mData;
    return *this;

   * Returns true if there are no more than @aBytes remaining in the
   * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
  bool RemainingBytesIsNoMoreThan(size_t aBytes) const;

   * Advances the iterator through the SourceBuffer if possible. If not,
   * arranges to call the @aConsumer's Resume() method when more data is
   * available.
  State AdvanceOrScheduleResume(IResumable* aConsumer);

  /// If at the end, returns the status passed to SourceBuffer::Complete().
  nsresult CompletionStatus() const
               "Calling CompletionStatus() in the wrong state");
    return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;

  /// If we're ready to read, returns a pointer to the new data.
  const char* Data() const
    MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
    return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
                           : nullptr;

  /// If we're ready to read, returns the length of the new data.
  size_t Length() const
    MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
    return mState == READY ? mData.mIterating.mLength : 0;

  friend class SourceBuffer;

  SourceBufferIterator(const SourceBufferIterator&) = delete;
  SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;

  bool HasMore() const { return mState != COMPLETE; }

  State SetReady(uint32_t aChunk, const char* aData,
                size_t aOffset, size_t aLength)
    mData.mIterating.mChunk = aChunk;
    mData.mIterating.mData = aData;
    mData.mIterating.mOffset = aOffset;
    mData.mIterating.mLength = aLength;
    return mState = READY;

  State SetWaiting()
    MOZ_ASSERT(mState != WAITING, "Did we get a spurious wakeup somehow?");
    return mState = WAITING;

  State SetComplete(nsresult aStatus)
    mData.mAtEnd.mStatus = aStatus;
    return mState = COMPLETE;

  RefPtr<SourceBuffer> mOwner;

  State mState;

   * This union contains our iteration state if we're still iterating (for
   * states START, READY, and WAITING) and the status the SourceBuffer was
   * completed with if we're in state COMPLETE.
  union {
    struct {
      uint32_t mChunk;
      const char* mData;
      size_t mOffset;
      size_t mLength;
    } mIterating;
    struct {
      nsresult mStatus;
    } mAtEnd;
  } mData;

 * SourceBuffer is a parallel data structure used for storing image source
 * (compressed) data.
 * SourceBuffer is a single producer, multiple consumer data structure. The
 * single producer calls Append() to append data to the buffer. In parallel,
 * multiple consumers can call Iterator(), which returns a SourceBufferIterator
 * that they can use to iterate through the buffer. The SourceBufferIterator
 * returns a series of pointers which remain stable for lifetime of the
 * SourceBuffer, and the data they point to is immutable, ensuring that the
 * producer never interferes with the consumers.
 * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to
 * keep a list of consumers which are waiting for new data, and to resume them
 * when the producer appends more. All consumers must implement the IResumable
 * interface to make this possible.
class SourceBuffer final


  // Producer methods.

   * If the producer knows how long the source data will be, it should call
   * ExpectLength, which enables SourceBuffer to preallocate its buffer.
  nsresult ExpectLength(size_t aExpectedLength);

  /// Append the provided data to the buffer.
  nsresult Append(const char* aData, size_t aLength);

  /// Append the data available on the provided nsIInputStream to the buffer.
  nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount);

   * Mark the buffer complete, with a status that will be available to
   * consumers. Further calls to Append() are forbidden after Complete().
  void Complete(nsresult aStatus);

  /// Returns true if the buffer is complete.
  bool IsComplete();

  /// Memory reporting.
  size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;

  // Consumer methods.

  /// Returns an iterator to this SourceBuffer.
  SourceBufferIterator Iterator();

  friend class SourceBufferIterator;


  // Chunk type and chunk-related methods.

  class Chunk
    explicit Chunk(size_t aCapacity)
      : mCapacity(aCapacity)
      , mLength(0)
      MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
      mData.reset(new (fallible) char[mCapacity]);

    Chunk(Chunk&& aOther)
      : mCapacity(aOther.mCapacity)
      , mLength(aOther.mLength)
      , mData(Move(aOther.mData))
      aOther.mCapacity = aOther.mLength = 0;
      aOther.mData = nullptr;

    Chunk& operator=(Chunk&& aOther)
      mCapacity = aOther.mCapacity;
      mLength = aOther.mLength;
      mData = Move(aOther.mData);
      aOther.mCapacity = aOther.mLength = 0;
      aOther.mData = nullptr;
      return *this;

    bool AllocationFailed() const { return !mData; }
    size_t Capacity() const { return mCapacity; }
    size_t Length() const { return mLength; }

    char* Data() const
      MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
      return mData.get();

    void AddLength(size_t aAdditionalLength)
      MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
      mLength += aAdditionalLength;

    Chunk(const Chunk&) = delete;
    Chunk& operator=(const Chunk&) = delete;

    size_t mCapacity;
    size_t mLength;
    UniquePtr<char[]> mData;

  nsresult AppendChunk(Maybe<Chunk>&& aChunk);
  Maybe<Chunk> CreateChunk(size_t aCapacity, bool aRoundUp = true);
  nsresult Compact();
  static size_t RoundedUpCapacity(size_t aCapacity);
  size_t FibonacciCapacityWithMinimum(size_t aMinCapacity);

  // Iterator / consumer methods.

  void AddWaitingConsumer(IResumable* aConsumer);
  void ResumeWaitingConsumers();

  typedef SourceBufferIterator::State State;

  State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
                                        IResumable* aConsumer);
  bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
                                  size_t aBytes) const;

  void OnIteratorRelease();

  // Helper methods.

  nsresult HandleError(nsresult aError);
  bool IsEmpty();
  bool IsLastChunk(uint32_t aChunk);

  // Member variables.

  static const size_t MIN_CHUNK_CAPACITY = 4096;

  /// All private members are protected by mMutex.
  mutable Mutex mMutex;

  /// The data in this SourceBuffer, stored as a series of Chunks.
  FallibleTArray<Chunk> mChunks;

  /// Consumers which are waiting to be notified when new data is available.
  nsTArray<RefPtr<IResumable>> mWaitingConsumers;

  /// If present, marks this SourceBuffer complete with the given final status.
  Maybe<nsresult> mStatus;

  /// Count of active consumers.
  uint32_t mConsumerCount;

} // namespace image
} // namespace mozilla

#endif // mozilla_image_sourcebuffer_h