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/. */
7 * SourceBuffer is a single producer, multiple consumer data structure used for
8 * storing image source (compressed) data.
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"
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.
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;
46 virtual ~IResumable() { }
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
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
)
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
)
94 ~SourceBufferIterator();
96 SourceBufferIterator
& operator=(SourceBufferIterator
&& aOther
)
98 mOwner
= Move(aOther
.mOwner
);
99 mState
= aOther
.mState
;
100 mData
= aOther
.mData
;
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
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
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;
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
;
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
;
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.
194 * SourceBuffer is a parallel data structure used for storing image source
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
216 class SourceBuffer final
219 MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer
)
220 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer
)
224 //////////////////////////////////////////////////////////////////////////////
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.
246 /// Memory reporting.
247 size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf
) const;
250 //////////////////////////////////////////////////////////////////////////////
252 //////////////////////////////////////////////////////////////////////////////
254 /// Returns an iterator to this SourceBuffer.
255 SourceBufferIterator
Iterator();
259 friend class SourceBufferIterator
;
263 //////////////////////////////////////////////////////////////////////////////
264 // Chunk type and chunk-related methods.
265 //////////////////////////////////////////////////////////////////////////////
270 explicit Chunk(size_t aCapacity
)
271 : mCapacity(aCapacity
)
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;
299 bool AllocationFailed() const { return !mData
; }
300 size_t Capacity() const { return mCapacity
; }
301 size_t Length() const { return mLength
; }
305 MOZ_ASSERT(mData
, "Allocation failed but nobody checked for it");
309 void AddLength(size_t aAdditionalLength
)
311 MOZ_ASSERT(mLength
+ aAdditionalLength
<= mCapacity
);
312 mLength
+= aAdditionalLength
;
316 Chunk(const Chunk
&) = delete;
317 Chunk
& operator=(const Chunk
&) = delete;
324 nsresult
AppendChunk(Maybe
<Chunk
>&& aChunk
);
325 Maybe
<Chunk
> CreateChunk(size_t aCapacity
, bool aRoundUp
= true);
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 //////////////////////////////////////////////////////////////////////////////
349 //////////////////////////////////////////////////////////////////////////////
351 nsresult
HandleError(nsresult aError
);
353 bool IsLastChunk(uint32_t aChunk
);
356 //////////////////////////////////////////////////////////////////////////////
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
;
379 } // namespace mozilla
381 #endif // MOZILLA_IMAGELIB_SOURCEBUFFER_H_