Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / image / src / SourceBuffer.h
blob074b38eadeff7f071845165066d840a53e27142b
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_IMAGELIB_SOURCEBUFFER_H_
12 #define MOZILLA_IMAGELIB_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/UniquePtr.h"
21 #include "nsRefPtr.h"
22 #include "nsTArray.h"
24 namespace mozilla {
25 namespace image {
27 class SourceBuffer;
29 /**
30 * IResumable is an interface for classes that can schedule themselves to resume
31 * their work later. An implementation of IResumable generally should post a
32 * runnable to some event target which continues the work of the task.
34 struct IResumable
36 MOZ_DECLARE_REFCOUNTED_TYPENAME(IResumable)
38 // Subclasses may or may not be XPCOM classes, so we just require that they
39 // implement AddRef and Release.
40 NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
41 NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
43 virtual void Resume() = 0;
45 protected:
46 virtual ~IResumable() { }
49 /**
50 * SourceBufferIterator is a class that allows consumers of image source data to
51 * read the contents of a SourceBuffer sequentially.
53 * Consumers can advance through the SourceBuffer by calling
54 * AdvanceOrScheduleResume() repeatedly. After every advance, they should call
55 * check the return value, which will tell them the iterator's new state.
57 * If WAITING is returned, AdvanceOrScheduleResume() has arranged
58 * to call the consumer's Resume() method later, so the consumer should save its
59 * state if needed and stop running.
61 * If the iterator's new state is READY, then the consumer can call Data() and
62 * Length() to read new data from the SourceBuffer.
64 * Finally, in the COMPLETE state the consumer can call CompletionStatus() to
65 * get the status passed to SourceBuffer::Complete().
67 class SourceBufferIterator final
69 public:
70 enum State {
71 START, // The iterator is at the beginning of the buffer.
72 READY, // The iterator is pointing to new data.
73 WAITING, // The iterator is blocked and the caller must yield.
74 COMPLETE // The iterator is pointing to the end of the buffer.
77 explicit SourceBufferIterator(SourceBuffer* aOwner)
78 : mOwner(aOwner)
79 , mState(START)
81 MOZ_ASSERT(aOwner);
82 mData.mIterating.mChunk = 0;
83 mData.mIterating.mData = nullptr;
84 mData.mIterating.mOffset = 0;
85 mData.mIterating.mLength = 0;
88 SourceBufferIterator(SourceBufferIterator&& aOther)
89 : mOwner(Move(aOther.mOwner))
90 , mState(aOther.mState)
91 , mData(aOther.mData)
92 { }
94 ~SourceBufferIterator();
96 SourceBufferIterator& operator=(SourceBufferIterator&& aOther)
98 mOwner = Move(aOther.mOwner);
99 mState = aOther.mState;
100 mData = aOther.mData;
101 return *this;
105 * Returns true if there are no more than @aBytes remaining in the
106 * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
108 bool RemainingBytesIsNoMoreThan(size_t aBytes) const;
111 * Advances the iterator through the SourceBuffer if possible. If not,
112 * arranges to call the @aConsumer's Resume() method when more data is
113 * available.
115 State AdvanceOrScheduleResume(IResumable* aConsumer);
117 /// If at the end, returns the status passed to SourceBuffer::Complete().
118 nsresult CompletionStatus() const
120 MOZ_ASSERT(mState == COMPLETE, "Calling CompletionStatus() in the wrong state");
121 return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;
124 /// If we're ready to read, returns a pointer to the new data.
125 const char* Data() const
127 MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
128 return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
129 : nullptr;
132 /// If we're ready to read, returns the length of the new data.
133 size_t Length() const
135 MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
136 return mState == READY ? mData.mIterating.mLength : 0;
139 private:
140 friend class SourceBuffer;
142 SourceBufferIterator(const SourceBufferIterator&) = delete;
143 SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
145 bool HasMore() const { return mState != COMPLETE; }
147 State SetReady(uint32_t aChunk, const char* aData,
148 size_t aOffset, size_t aLength)
150 MOZ_ASSERT(mState != COMPLETE);
151 mData.mIterating.mChunk = aChunk;
152 mData.mIterating.mData = aData;
153 mData.mIterating.mOffset = aOffset;
154 mData.mIterating.mLength = aLength;
155 return mState = READY;
158 State SetWaiting()
160 MOZ_ASSERT(mState != COMPLETE);
161 MOZ_ASSERT(mState != WAITING, "Did we get a spurious wakeup somehow?");
162 return mState = WAITING;
165 State SetComplete(nsresult aStatus)
167 mData.mAtEnd.mStatus = aStatus;
168 return mState = COMPLETE;
171 nsRefPtr<SourceBuffer> mOwner;
173 State mState;
176 * This union contains our iteration state if we're still iterating (for
177 * states START, READY, and WAITING) and the status the SourceBuffer was
178 * completed with if we're in state COMPLETE.
180 union {
181 struct {
182 uint32_t mChunk;
183 const char* mData;
184 size_t mOffset;
185 size_t mLength;
186 } mIterating;
187 struct {
188 nsresult mStatus;
189 } mAtEnd;
190 } mData;
194 * SourceBuffer is a parallel data structure used for storing image source
195 * (compressed) data.
197 * SourceBuffer is a single producer, multiple consumer data structure. The
198 * single producer calls Append() to append data to the buffer. In parallel,
199 * multiple consumers can call Iterator(), which returns a SourceBufferIterator
200 * that they can use to iterate through the buffer. The SourceBufferIterator
201 * returns a series of pointers which remain stable for lifetime of the
202 * SourceBuffer, and the data they point to is immutable, ensuring that the
203 * producer never interferes with the consumers.
205 * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to
206 * keep a list of consumers which are waiting for new data, and to resume them
207 * when the producer appends more. All consumers must implement the IResumable
208 * interface to make this possible.
210 * XXX(seth): We should add support for compacting a SourceBuffer. To do this,
211 * we need to have SourceBuffer keep track of how many live
212 * SourceBufferIterator's point to it. When the SourceBuffer is complete and no
213 * live SourceBufferIterator's for it remain, we can compact its contents into a
214 * single chunk.
216 class SourceBuffer final
218 public:
219 MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
220 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
222 SourceBuffer();
224 //////////////////////////////////////////////////////////////////////////////
225 // Producer methods.
226 //////////////////////////////////////////////////////////////////////////////
229 * If the producer knows how long the source data will be, it should call
230 * ExpectLength, which enables SourceBuffer to preallocate its buffer.
232 nsresult ExpectLength(size_t aExpectedLength);
234 /// Append the provided data to the buffer.
235 nsresult Append(const char* aData, size_t aLength);
238 * Mark the buffer complete, with a status that will be available to
239 * consumers. Further calls to Append() are forbidden after Complete().
241 void Complete(nsresult aStatus);
243 /// Returns true if the buffer is complete.
244 bool IsComplete();
246 /// Memory reporting.
247 size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
250 //////////////////////////////////////////////////////////////////////////////
251 // Consumer methods.
252 //////////////////////////////////////////////////////////////////////////////
254 /// Returns an iterator to this SourceBuffer.
255 SourceBufferIterator Iterator();
258 private:
259 friend class SourceBufferIterator;
261 ~SourceBuffer();
263 //////////////////////////////////////////////////////////////////////////////
264 // Chunk type and chunk-related methods.
265 //////////////////////////////////////////////////////////////////////////////
267 class Chunk
269 public:
270 explicit Chunk(size_t aCapacity)
271 : mCapacity(aCapacity)
272 , mLength(0)
274 MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
275 mData = new (fallible) char[mCapacity];
278 ~Chunk() { delete[] mData; }
280 Chunk(Chunk&& aOther)
281 : mCapacity(aOther.mCapacity)
282 , mLength(aOther.mLength)
283 , mData(aOther.mData)
285 aOther.mCapacity = aOther.mLength = 0;
286 aOther.mData = nullptr;
289 Chunk& operator=(Chunk&& aOther)
291 mCapacity = aOther.mCapacity;
292 mLength = aOther.mLength;
293 mData = aOther.mData;
294 aOther.mCapacity = aOther.mLength = 0;
295 aOther.mData = nullptr;
296 return *this;
299 bool AllocationFailed() const { return !mData; }
300 size_t Capacity() const { return mCapacity; }
301 size_t Length() const { return mLength; }
303 char* Data() const
305 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
306 return mData;
309 void AddLength(size_t aAdditionalLength)
311 MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
312 mLength += aAdditionalLength;
315 private:
316 Chunk(const Chunk&) = delete;
317 Chunk& operator=(const Chunk&) = delete;
319 size_t mCapacity;
320 size_t mLength;
321 char* mData;
324 nsresult AppendChunk(Maybe<Chunk>&& aChunk);
325 Maybe<Chunk> CreateChunk(size_t aCapacity, bool aRoundUp = true);
326 nsresult Compact();
327 static size_t RoundedUpCapacity(size_t aCapacity);
328 size_t FibonacciCapacityWithMinimum(size_t aMinCapacity);
331 //////////////////////////////////////////////////////////////////////////////
332 // Iterator / consumer methods.
333 //////////////////////////////////////////////////////////////////////////////
335 void AddWaitingConsumer(IResumable* aConsumer);
336 void ResumeWaitingConsumers();
338 typedef SourceBufferIterator::State State;
340 State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
341 IResumable* aConsumer);
342 bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
343 size_t aBytes) const;
345 void OnIteratorRelease();
347 //////////////////////////////////////////////////////////////////////////////
348 // Helper methods.
349 //////////////////////////////////////////////////////////////////////////////
351 nsresult HandleError(nsresult aError);
352 bool IsEmpty();
353 bool IsLastChunk(uint32_t aChunk);
356 //////////////////////////////////////////////////////////////////////////////
357 // Member variables.
358 //////////////////////////////////////////////////////////////////////////////
360 static const size_t MIN_CHUNK_CAPACITY = 4096;
362 /// All private members are protected by mMutex.
363 mutable Mutex mMutex;
365 /// The data in this SourceBuffer, stored as a series of Chunks.
366 FallibleTArray<Chunk> mChunks;
368 /// Consumers which are waiting to be notified when new data is available.
369 nsTArray<nsRefPtr<IResumable>> mWaitingConsumers;
371 /// If present, marks this SourceBuffer complete with the given final status.
372 Maybe<nsresult> mStatus;
374 /// Count of active consumers.
375 uint32_t mConsumerCount;
378 } // namespace image
379 } // namespace mozilla
381 #endif // MOZILLA_IMAGELIB_SOURCEBUFFER_H_