Backed out changeset 177eae915693 (bug 1206581) for bustage
[gecko.git] / image / SourceBuffer.h
blobf006f718147ccef4aafaf11a35729710a6c576fe
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/. */
6 /**
7 * SourceBuffer is a single producer, multiple consumer data structure used for
8 * storing image source (compressed) data.
9 */
11 #ifndef mozilla_image_sourcebuffer_h
12 #define mozilla_image_sourcebuffer_h
14 #include "mozilla/Maybe.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/Mutex.h"
17 #include "mozilla/Move.h"
18 #include "mozilla/MemoryReporting.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/RefCounted.h"
21 #include "mozilla/UniquePtr.h"
22 #include "mozilla/RefPtr.h"
23 #include "nsTArray.h"
25 class nsIInputStream;
27 namespace mozilla {
28 namespace image {
30 class SourceBuffer;
32 /**
33 * IResumable is an interface for classes that can schedule themselves to resume
34 * their work later. An implementation of IResumable generally should post a
35 * runnable to some event target which continues the work of the task.
37 struct IResumable
39 MOZ_DECLARE_REFCOUNTED_TYPENAME(IResumable)
41 // Subclasses may or may not be XPCOM classes, so we just require that they
42 // implement AddRef and Release.
43 NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
44 NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
46 virtual void Resume() = 0;
48 protected:
49 virtual ~IResumable() { }
52 /**
53 * SourceBufferIterator is a class that allows consumers of image source data to
54 * read the contents of a SourceBuffer sequentially.
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.
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.
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.
67 * Finally, in the COMPLETE state the consumer can call CompletionStatus() to
68 * get the status passed to SourceBuffer::Complete().
70 class SourceBufferIterator final
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.
80 explicit SourceBufferIterator(SourceBuffer* aOwner)
81 : mOwner(aOwner)
82 , mState(START)
84 MOZ_ASSERT(aOwner);
85 mData.mIterating.mChunk = 0;
86 mData.mIterating.mData = nullptr;
87 mData.mIterating.mOffset = 0;
88 mData.mIterating.mLength = 0;
91 SourceBufferIterator(SourceBufferIterator&& aOther)
92 : mOwner(Move(aOther.mOwner))
93 , mState(aOther.mState)
94 , mData(aOther.mData)
95 { }
97 ~SourceBufferIterator();
99 SourceBufferIterator& operator=(SourceBufferIterator&& aOther)
101 mOwner = Move(aOther.mOwner);
102 mState = aOther.mState;
103 mData = aOther.mData;
104 return *this;
108 * Returns true if there are no more than @aBytes remaining in the
109 * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
111 bool RemainingBytesIsNoMoreThan(size_t aBytes) const;
114 * Advances the iterator through the SourceBuffer if possible. If not,
115 * arranges to call the @aConsumer's Resume() method when more data is
116 * available.
118 State AdvanceOrScheduleResume(IResumable* aConsumer);
120 /// If at the end, returns the status passed to SourceBuffer::Complete().
121 nsresult CompletionStatus() const
123 MOZ_ASSERT(mState == COMPLETE,
124 "Calling CompletionStatus() in the wrong state");
125 return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;
128 /// If we're ready to read, returns a pointer to the new data.
129 const char* Data() const
131 MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
132 return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
133 : nullptr;
136 /// If we're ready to read, returns the length of the new data.
137 size_t Length() const
139 MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
140 return mState == READY ? mData.mIterating.mLength : 0;
143 private:
144 friend class SourceBuffer;
146 SourceBufferIterator(const SourceBufferIterator&) = delete;
147 SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
149 bool HasMore() const { return mState != COMPLETE; }
151 State SetReady(uint32_t aChunk, const char* aData,
152 size_t aOffset, size_t aLength)
154 MOZ_ASSERT(mState != COMPLETE);
155 mData.mIterating.mChunk = aChunk;
156 mData.mIterating.mData = aData;
157 mData.mIterating.mOffset = aOffset;
158 mData.mIterating.mLength = aLength;
159 return mState = READY;
162 State SetWaiting()
164 MOZ_ASSERT(mState != COMPLETE);
165 MOZ_ASSERT(mState != WAITING, "Did we get a spurious wakeup somehow?");
166 return mState = WAITING;
169 State SetComplete(nsresult aStatus)
171 mData.mAtEnd.mStatus = aStatus;
172 return mState = COMPLETE;
175 RefPtr<SourceBuffer> mOwner;
177 State mState;
180 * This union contains our iteration state if we're still iterating (for
181 * states START, READY, and WAITING) and the status the SourceBuffer was
182 * completed with if we're in state COMPLETE.
184 union {
185 struct {
186 uint32_t mChunk;
187 const char* mData;
188 size_t mOffset;
189 size_t mLength;
190 } mIterating;
191 struct {
192 nsresult mStatus;
193 } mAtEnd;
194 } mData;
198 * SourceBuffer is a parallel data structure used for storing image source
199 * (compressed) data.
201 * SourceBuffer is a single producer, multiple consumer data structure. The
202 * single producer calls Append() to append data to the buffer. In parallel,
203 * multiple consumers can call Iterator(), which returns a SourceBufferIterator
204 * that they can use to iterate through the buffer. The SourceBufferIterator
205 * returns a series of pointers which remain stable for lifetime of the
206 * SourceBuffer, and the data they point to is immutable, ensuring that the
207 * producer never interferes with the consumers.
209 * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to
210 * keep a list of consumers which are waiting for new data, and to resume them
211 * when the producer appends more. All consumers must implement the IResumable
212 * interface to make this possible.
214 class SourceBuffer final
216 public:
217 MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
218 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
220 SourceBuffer();
222 //////////////////////////////////////////////////////////////////////////////
223 // Producer methods.
224 //////////////////////////////////////////////////////////////////////////////
227 * If the producer knows how long the source data will be, it should call
228 * ExpectLength, which enables SourceBuffer to preallocate its buffer.
230 nsresult ExpectLength(size_t aExpectedLength);
232 /// Append the provided data to the buffer.
233 nsresult Append(const char* aData, size_t aLength);
235 /// Append the data available on the provided nsIInputStream to the buffer.
236 nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount);
239 * Mark the buffer complete, with a status that will be available to
240 * consumers. Further calls to Append() are forbidden after Complete().
242 void Complete(nsresult aStatus);
244 /// Returns true if the buffer is complete.
245 bool IsComplete();
247 /// Memory reporting.
248 size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
251 //////////////////////////////////////////////////////////////////////////////
252 // Consumer methods.
253 //////////////////////////////////////////////////////////////////////////////
255 /// Returns an iterator to this SourceBuffer.
256 SourceBufferIterator Iterator();
259 private:
260 friend class SourceBufferIterator;
262 ~SourceBuffer();
264 //////////////////////////////////////////////////////////////////////////////
265 // Chunk type and chunk-related methods.
266 //////////////////////////////////////////////////////////////////////////////
268 class Chunk
270 public:
271 explicit Chunk(size_t aCapacity)
272 : mCapacity(aCapacity)
273 , mLength(0)
275 MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
276 mData.reset(new (fallible) char[mCapacity]);
279 Chunk(Chunk&& aOther)
280 : mCapacity(aOther.mCapacity)
281 , mLength(aOther.mLength)
282 , mData(Move(aOther.mData))
284 aOther.mCapacity = aOther.mLength = 0;
285 aOther.mData = nullptr;
288 Chunk& operator=(Chunk&& aOther)
290 mCapacity = aOther.mCapacity;
291 mLength = aOther.mLength;
292 mData = Move(aOther.mData);
293 aOther.mCapacity = aOther.mLength = 0;
294 aOther.mData = nullptr;
295 return *this;
298 bool AllocationFailed() const { return !mData; }
299 size_t Capacity() const { return mCapacity; }
300 size_t Length() const { return mLength; }
302 char* Data() const
304 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
305 return mData.get();
308 void AddLength(size_t aAdditionalLength)
310 MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
311 mLength += aAdditionalLength;
314 private:
315 Chunk(const Chunk&) = delete;
316 Chunk& operator=(const Chunk&) = delete;
318 size_t mCapacity;
319 size_t mLength;
320 UniquePtr<char[]> mData;
323 nsresult AppendChunk(Maybe<Chunk>&& aChunk);
324 Maybe<Chunk> CreateChunk(size_t aCapacity, bool aRoundUp = true);
325 nsresult Compact();
326 static size_t RoundedUpCapacity(size_t aCapacity);
327 size_t FibonacciCapacityWithMinimum(size_t aMinCapacity);
330 //////////////////////////////////////////////////////////////////////////////
331 // Iterator / consumer methods.
332 //////////////////////////////////////////////////////////////////////////////
334 void AddWaitingConsumer(IResumable* aConsumer);
335 void ResumeWaitingConsumers();
337 typedef SourceBufferIterator::State State;
339 State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
340 IResumable* aConsumer);
341 bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
342 size_t aBytes) const;
344 void OnIteratorRelease();
346 //////////////////////////////////////////////////////////////////////////////
347 // Helper methods.
348 //////////////////////////////////////////////////////////////////////////////
350 nsresult HandleError(nsresult aError);
351 bool IsEmpty();
352 bool IsLastChunk(uint32_t aChunk);
355 //////////////////////////////////////////////////////////////////////////////
356 // Member variables.
357 //////////////////////////////////////////////////////////////////////////////
359 static const size_t MIN_CHUNK_CAPACITY = 4096;
361 /// All private members are protected by mMutex.
362 mutable Mutex mMutex;
364 /// The data in this SourceBuffer, stored as a series of Chunks.
365 FallibleTArray<Chunk> mChunks;
367 /// Consumers which are waiting to be notified when new data is available.
368 nsTArray<RefPtr<IResumable>> mWaitingConsumers;
370 /// If present, marks this SourceBuffer complete with the given final status.
371 Maybe<nsresult> mStatus;
373 /// Count of active consumers.
374 uint32_t mConsumerCount;
377 } // namespace image
378 } // namespace mozilla
380 #endif // mozilla_image_sourcebuffer_h