123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- /* -*- 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 http://mozilla.org/MPL/2.0/. */
- /**
- * 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 <algorithm>
- #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
- {
- MOZ_DECLARE_REFCOUNTED_TYPENAME(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;
- protected:
- 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
- {
- public:
- 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)
- , mChunkCount(0)
- , mByteCount(0)
- {
- MOZ_ASSERT(aOwner);
- mData.mIterating.mChunk = 0;
- mData.mIterating.mData = nullptr;
- mData.mIterating.mOffset = 0;
- mData.mIterating.mAvailableLength = 0;
- mData.mIterating.mNextReadLength = 0;
- }
- SourceBufferIterator(SourceBufferIterator&& aOther)
- : mOwner(Move(aOther.mOwner))
- , mState(aOther.mState)
- , mData(aOther.mData)
- , mChunkCount(aOther.mChunkCount)
- , mByteCount(aOther.mByteCount)
- { }
- ~SourceBufferIterator();
- SourceBufferIterator& operator=(SourceBufferIterator&& aOther);
- /**
- * 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. Advances no
- * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
- * possible.)
- *
- * This is a wrapper around AdvanceOrScheduleResume() that makes it clearer at
- * the callsite when the no resuming is intended.
- *
- * @return State::READY if the iterator was successfully advanced.
- * State::WAITING if the iterator could not be advanced because it's
- * at the end of the underlying SourceBuffer, but the SourceBuffer
- * may still receive additional data.
- * State::COMPLETE if the iterator could not be advanced because it's
- * at the end of the underlying SourceBuffer and the SourceBuffer is
- * marked complete (i.e., it will never receive any additional
- * data).
- */
- State Advance(size_t aRequestedBytes)
- {
- return AdvanceOrScheduleResume(aRequestedBytes, nullptr);
- }
- /**
- * Advances the iterator through the SourceBuffer if possible. Advances no
- * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
- * possible.) If advancing is not possible and @aConsumer is not null,
- * arranges to call the @aConsumer's Resume() method when more data is
- * available.
- *
- * @return State::READY if the iterator was successfully advanced.
- * State::WAITING if the iterator could not be advanced because it's
- * at the end of the underlying SourceBuffer, but the SourceBuffer
- * may still receive additional data. @aConsumer's Resume() method
- * will be called when additional data is available.
- * State::COMPLETE if the iterator could not be advanced because it's
- * at the end of the underlying SourceBuffer and the SourceBuffer is
- * marked complete (i.e., it will never receive any additional
- * data).
- */
- State AdvanceOrScheduleResume(size_t aRequestedBytes, IResumable* aConsumer);
- /// If at the end, returns the status passed to SourceBuffer::Complete().
- nsresult CompletionStatus() const
- {
- MOZ_ASSERT(mState == COMPLETE,
- "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.mNextReadLength : 0;
- }
- /// If we're ready to read, returns whether or not everything available thus
- /// far has been in the same contiguous buffer.
- bool IsContiguous() const {
- MOZ_ASSERT(mState == READY, "Calling IsContiguous() in the wrong state");
- return mState == READY ? mData.mIterating.mChunk == 0 : false;
- }
-
- /// @return a count of the chunks we've advanced through.
- uint32_t ChunkCount() const { return mChunkCount; }
- /// @return a count of the bytes in all chunks we've advanced through.
- size_t ByteCount() const { return mByteCount; }
- private:
- friend class SourceBuffer;
- SourceBufferIterator(const SourceBufferIterator&) = delete;
- SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
- bool HasMore() const { return mState != COMPLETE; }
- State AdvanceFromLocalBuffer(size_t aRequestedBytes)
- {
- MOZ_ASSERT(mState == READY, "Advancing in the wrong state");
- MOZ_ASSERT(mData.mIterating.mAvailableLength > 0,
- "The local buffer shouldn't be empty");
- MOZ_ASSERT(mData.mIterating.mNextReadLength == 0,
- "Advancing without consuming previous data");
- mData.mIterating.mNextReadLength =
- std::min(mData.mIterating.mAvailableLength, aRequestedBytes);
- return READY;
- }
- State SetReady(uint32_t aChunk, const char* aData,
- size_t aOffset, size_t aAvailableLength,
- size_t aRequestedBytes)
- {
- MOZ_ASSERT(mState != COMPLETE);
- mState = READY;
- // Update state.
- mData.mIterating.mChunk = aChunk;
- mData.mIterating.mData = aData;
- mData.mIterating.mOffset = aOffset;
- mData.mIterating.mAvailableLength = aAvailableLength;
- // Update metrics.
- mChunkCount++;
- mByteCount += aAvailableLength;
- // Attempt to advance by the requested number of bytes.
- return AdvanceFromLocalBuffer(aRequestedBytes);
- }
- State SetWaiting()
- {
- MOZ_ASSERT(mState != COMPLETE);
- 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 mAvailableLength;
- size_t mNextReadLength;
- } mIterating;
- struct {
- nsresult mStatus;
- } mAtEnd;
- } mData;
- uint32_t mChunkCount; // Count of chunks we've advanced through.
- size_t mByteCount; // Count of bytes in all chunks we've advanced through.
- };
- /**
- * 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
- {
- public:
- MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
- SourceBuffer();
- //////////////////////////////////////////////////////////////////////////////
- // 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();
- //////////////////////////////////////////////////////////////////////////////
- // Consumer methods.
- //////////////////////////////////////////////////////////////////////////////
- /**
- * The minimum chunk capacity we'll allocate, if we don't know the correct
- * capacity (which would happen because ExpectLength() wasn't called or gave
- * us the wrong value). This is only exposed for use by tests; if normal code
- * is using this, it's doing something wrong.
- */
- static const size_t MIN_CHUNK_CAPACITY = 4096;
- private:
- friend class SourceBufferIterator;
- ~SourceBuffer();
- //////////////////////////////////////////////////////////////////////////////
- // Chunk type and chunk-related methods.
- //////////////////////////////////////////////////////////////////////////////
- class Chunk
- {
- public:
- 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;
- }
- private:
- 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,
- size_t aRequestedBytes,
- 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.
- //////////////////////////////////////////////////////////////////////////////
- /// 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
|