Bug 1796551 [wpt PR 36570] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / image / SourceBuffer.h
blob79188fb88227167cb3a78c0ff4729e423ceaba0f
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 <algorithm>
15 #include <utility>
17 #include "mozilla/Maybe.h"
18 #include "mozilla/MemoryReporting.h"
19 #include "mozilla/Mutex.h"
20 #include "mozilla/RefCounted.h"
21 #include "mozilla/RefPtr.h"
22 #include "mozilla/UniquePtr.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 {
38 MOZ_DECLARE_REFCOUNTED_TYPENAME(IResumable)
40 // Subclasses may or may not be XPCOM classes, so we just require that they
41 // implement AddRef and Release.
42 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
44 virtual void Resume() = 0;
46 protected:
47 virtual ~IResumable() {}
50 /**
51 * SourceBufferIterator is a class that allows consumers of image source data to
52 * read the contents of a SourceBuffer sequentially.
54 * Consumers can advance through the SourceBuffer by calling
55 * AdvanceOrScheduleResume() repeatedly. After every advance, they should call
56 * check the return value, which will tell them the iterator's new state.
58 * If WAITING is returned, AdvanceOrScheduleResume() has arranged
59 * to call the consumer's Resume() method later, so the consumer should save its
60 * state if needed and stop running.
62 * If the iterator's new state is READY, then the consumer can call Data() and
63 * Length() to read new data from the SourceBuffer.
65 * Finally, in the COMPLETE state the consumer can call CompletionStatus() to
66 * get the status passed to SourceBuffer::Complete().
68 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, size_t aReadLimit)
78 : mOwner(aOwner),
79 mState(START),
80 mChunkCount(0),
81 mByteCount(0),
82 mRemainderToRead(aReadLimit) {
83 MOZ_ASSERT(aOwner);
84 mData.mIterating.mChunk = 0;
85 mData.mIterating.mData = nullptr;
86 mData.mIterating.mOffset = 0;
87 mData.mIterating.mAvailableLength = 0;
88 mData.mIterating.mNextReadLength = 0;
91 SourceBufferIterator(SourceBufferIterator&& aOther)
92 : mOwner(std::move(aOther.mOwner)),
93 mState(aOther.mState),
94 mData(aOther.mData),
95 mChunkCount(aOther.mChunkCount),
96 mByteCount(aOther.mByteCount),
97 mRemainderToRead(aOther.mRemainderToRead) {}
99 ~SourceBufferIterator();
101 SourceBufferIterator& operator=(SourceBufferIterator&& aOther);
104 * Returns true if there are no more than @aBytes remaining in the
105 * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
107 bool RemainingBytesIsNoMoreThan(size_t aBytes) const;
110 * Advances the iterator through the SourceBuffer if possible. Advances no
111 * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
112 * possible.)
114 * This is a wrapper around AdvanceOrScheduleResume() that makes it clearer at
115 * the callsite when the no resuming is intended.
117 * @return State::READY if the iterator was successfully advanced.
118 * State::WAITING if the iterator could not be advanced because it's
119 * at the end of the underlying SourceBuffer, but the SourceBuffer
120 * may still receive additional data.
121 * State::COMPLETE if the iterator could not be advanced because it's
122 * at the end of the underlying SourceBuffer and the SourceBuffer is
123 * marked complete (i.e., it will never receive any additional
124 * data).
126 State Advance(size_t aRequestedBytes) {
127 return AdvanceOrScheduleResume(aRequestedBytes, nullptr);
131 * Advances the iterator through the SourceBuffer if possible. Advances no
132 * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
133 * possible.) If advancing is not possible and @aConsumer is not null,
134 * arranges to call the @aConsumer's Resume() method when more data is
135 * available.
137 * @return State::READY if the iterator was successfully advanced.
138 * State::WAITING if the iterator could not be advanced because it's
139 * at the end of the underlying SourceBuffer, but the SourceBuffer
140 * may still receive additional data. @aConsumer's Resume() method
141 * will be called when additional data is available.
142 * State::COMPLETE if the iterator could not be advanced because it's
143 * at the end of the underlying SourceBuffer and the SourceBuffer is
144 * marked complete (i.e., it will never receive any additional
145 * data).
147 State AdvanceOrScheduleResume(size_t aRequestedBytes, IResumable* aConsumer);
149 /// If at the end, returns the status passed to SourceBuffer::Complete().
150 nsresult CompletionStatus() const {
151 MOZ_ASSERT(mState == COMPLETE,
152 "Calling CompletionStatus() in the wrong state");
153 return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;
156 /// If we're ready to read, returns a pointer to the new data.
157 const char* Data() const {
158 MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
159 return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
160 : nullptr;
163 /// If we're ready to read, returns the length of the new data.
164 size_t Length() const {
165 MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
166 return mState == READY ? mData.mIterating.mNextReadLength : 0;
169 /// If we're ready to read, returns whether or not everything available thus
170 /// far has been in the same contiguous buffer.
171 bool IsContiguous() const {
172 MOZ_ASSERT(mState == READY, "Calling IsContiguous() in the wrong state");
173 return mState == READY ? mData.mIterating.mChunk == 0 : false;
176 /// @return a count of the chunks we've advanced through.
177 uint32_t ChunkCount() const { return mChunkCount; }
179 /// @return a count of the bytes in all chunks we've advanced through.
180 size_t ByteCount() const { return mByteCount; }
182 /// @return the source buffer which owns the iterator.
183 SourceBuffer* Owner() const {
184 MOZ_ASSERT(mOwner);
185 return mOwner;
188 /// @return the current offset from the beginning of the buffer.
189 size_t Position() const {
190 return mByteCount - mData.mIterating.mAvailableLength;
193 private:
194 friend class SourceBuffer;
196 SourceBufferIterator(const SourceBufferIterator&) = delete;
197 SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
199 bool HasMore() const { return mState != COMPLETE; }
201 State AdvanceFromLocalBuffer(size_t aRequestedBytes) {
202 MOZ_ASSERT(mState == READY, "Advancing in the wrong state");
203 MOZ_ASSERT(mData.mIterating.mAvailableLength > 0,
204 "The local buffer shouldn't be empty");
205 MOZ_ASSERT(mData.mIterating.mNextReadLength == 0,
206 "Advancing without consuming previous data");
208 mData.mIterating.mNextReadLength =
209 std::min(mData.mIterating.mAvailableLength, aRequestedBytes);
211 return READY;
214 State SetReady(uint32_t aChunk, const char* aData, size_t aOffset,
215 size_t aAvailableLength, size_t aRequestedBytes) {
216 MOZ_ASSERT(mState != COMPLETE);
217 mState = READY;
219 // Prevent the iterator from reporting more data than it is allowed to read.
220 if (aAvailableLength > mRemainderToRead) {
221 aAvailableLength = mRemainderToRead;
224 // Update state.
225 mData.mIterating.mChunk = aChunk;
226 mData.mIterating.mData = aData;
227 mData.mIterating.mOffset = aOffset;
228 mData.mIterating.mAvailableLength = aAvailableLength;
230 // Update metrics.
231 mChunkCount++;
232 mByteCount += aAvailableLength;
234 // Attempt to advance by the requested number of bytes.
235 return AdvanceFromLocalBuffer(aRequestedBytes);
238 State SetWaiting(bool aHasConsumer) {
239 MOZ_ASSERT(mState != COMPLETE);
240 // Without a consumer, we won't know when to wake up precisely. Caller
241 // convention should mean that we don't try to advance unless we have
242 // written new data, but that doesn't mean we got enough.
243 MOZ_ASSERT(mState != WAITING || !aHasConsumer,
244 "Did we get a spurious wakeup somehow?");
245 return mState = WAITING;
248 State SetComplete(nsresult aStatus) {
249 mData.mAtEnd.mStatus = aStatus;
250 return mState = COMPLETE;
253 RefPtr<SourceBuffer> mOwner;
255 State mState;
258 * This union contains our iteration state if we're still iterating (for
259 * states START, READY, and WAITING) and the status the SourceBuffer was
260 * completed with if we're in state COMPLETE.
262 union {
263 struct {
264 uint32_t mChunk; // Index of the chunk in SourceBuffer.
265 const char* mData; // Pointer to the start of the chunk.
266 size_t mOffset; // Current read position of the iterator relative to
267 // mData.
268 size_t mAvailableLength; // How many bytes remain unread in the chunk,
269 // relative to mOffset.
270 size_t
271 mNextReadLength; // How many bytes the last iterator advance
272 // requested to be read, so that we know much
273 // to increase mOffset and reduce mAvailableLength
274 // by when the next advance is requested.
275 } mIterating; // Cached info of the chunk currently iterating over.
276 struct {
277 nsresult mStatus; // Status code indicating if we read all the data.
278 } mAtEnd; // State info after iterator is complete.
279 } mData;
281 uint32_t mChunkCount; // Count of chunks observed, including current chunk.
282 size_t mByteCount; // Count of readable bytes observed, including unread
283 // bytes from the current chunk.
284 size_t mRemainderToRead; // Count of bytes left to read if there is a maximum
285 // imposed by the caller. SIZE_MAX if unlimited.
289 * SourceBuffer is a parallel data structure used for storing image source
290 * (compressed) data.
292 * SourceBuffer is a single producer, multiple consumer data structure. The
293 * single producer calls Append() to append data to the buffer. In parallel,
294 * multiple consumers can call Iterator(), which returns a SourceBufferIterator
295 * that they can use to iterate through the buffer. The SourceBufferIterator
296 * returns a series of pointers which remain stable for lifetime of the
297 * SourceBuffer, and the data they point to is immutable, ensuring that the
298 * producer never interferes with the consumers.
300 * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to
301 * keep a list of consumers which are waiting for new data, and to resume them
302 * when the producer appends more. All consumers must implement the IResumable
303 * interface to make this possible.
305 class SourceBuffer final {
306 public:
307 MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
308 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
310 SourceBuffer();
312 //////////////////////////////////////////////////////////////////////////////
313 // Producer methods.
314 //////////////////////////////////////////////////////////////////////////////
317 * If the producer knows how long the source data will be, it should call
318 * ExpectLength, which enables SourceBuffer to preallocate its buffer.
320 nsresult ExpectLength(size_t aExpectedLength);
322 /// Append the provided data to the buffer.
323 nsresult Append(const char* aData, size_t aLength);
325 /// Append the data available on the provided nsIInputStream to the buffer.
326 nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount);
329 * Mark the buffer complete, with a status that will be available to
330 * consumers. Further calls to Append() are forbidden after Complete().
332 void Complete(nsresult aStatus);
334 /// Returns true if the buffer is complete.
335 bool IsComplete();
337 /// Memory reporting.
338 size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
340 //////////////////////////////////////////////////////////////////////////////
341 // Consumer methods.
342 //////////////////////////////////////////////////////////////////////////////
345 * Returns an iterator to this SourceBuffer, which cannot read more than the
346 * given length.
348 SourceBufferIterator Iterator(size_t aReadLength = SIZE_MAX);
350 //////////////////////////////////////////////////////////////////////////////
351 // Consumer methods.
352 //////////////////////////////////////////////////////////////////////////////
355 * The minimum chunk capacity we'll allocate, if we don't know the correct
356 * capacity (which would happen because ExpectLength() wasn't called or gave
357 * us the wrong value). This is only exposed for use by tests; if normal code
358 * is using this, it's doing something wrong.
360 static const size_t MIN_CHUNK_CAPACITY = 4096;
363 * The maximum chunk capacity we'll allocate. This was historically the
364 * maximum we would preallocate based on the network size. We may adjust it
365 * in the future based on the IMAGE_DECODE_CHUNKS telemetry to ensure most
366 * images remain in a single chunk.
368 static const size_t MAX_CHUNK_CAPACITY = 20 * 1024 * 1024;
370 private:
371 friend class SourceBufferIterator;
373 ~SourceBuffer();
375 //////////////////////////////////////////////////////////////////////////////
376 // Chunk type and chunk-related methods.
377 //////////////////////////////////////////////////////////////////////////////
379 class Chunk final {
380 public:
381 explicit Chunk(size_t aCapacity) : mCapacity(aCapacity), mLength(0) {
382 MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
383 mData = static_cast<char*>(malloc(mCapacity));
386 ~Chunk() { free(mData); }
388 Chunk(Chunk&& aOther)
389 : mCapacity(aOther.mCapacity),
390 mLength(aOther.mLength),
391 mData(aOther.mData) {
392 aOther.mCapacity = aOther.mLength = 0;
393 aOther.mData = nullptr;
396 Chunk& operator=(Chunk&& aOther) {
397 free(mData);
398 mCapacity = aOther.mCapacity;
399 mLength = aOther.mLength;
400 mData = aOther.mData;
401 aOther.mCapacity = aOther.mLength = 0;
402 aOther.mData = nullptr;
403 return *this;
406 bool AllocationFailed() const { return !mData; }
407 size_t Capacity() const { return mCapacity; }
408 size_t Length() const { return mLength; }
410 char* Data() const {
411 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
412 return mData;
415 void AddLength(size_t aAdditionalLength) {
416 MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
417 mLength += aAdditionalLength;
420 bool SetCapacity(size_t aCapacity) {
421 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
422 char* data = static_cast<char*>(realloc(mData, aCapacity));
423 if (!data) {
424 return false;
427 mData = data;
428 mCapacity = aCapacity;
429 return true;
432 private:
433 Chunk(const Chunk&) = delete;
434 Chunk& operator=(const Chunk&) = delete;
436 size_t mCapacity;
437 size_t mLength;
438 char* mData;
441 nsresult AppendChunk(Maybe<Chunk>&& aChunk) MOZ_REQUIRES(mMutex);
442 Maybe<Chunk> CreateChunk(size_t aCapacity, size_t aExistingCapacity = 0,
443 bool aRoundUp = true);
444 nsresult Compact() MOZ_REQUIRES(mMutex);
445 static size_t RoundedUpCapacity(size_t aCapacity);
446 size_t FibonacciCapacityWithMinimum(size_t aMinCapacity) MOZ_REQUIRES(mMutex);
448 //////////////////////////////////////////////////////////////////////////////
449 // Iterator / consumer methods.
450 //////////////////////////////////////////////////////////////////////////////
452 void AddWaitingConsumer(IResumable* aConsumer) MOZ_REQUIRES(mMutex);
453 void ResumeWaitingConsumers() MOZ_REQUIRES(mMutex);
455 typedef SourceBufferIterator::State State;
457 State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
458 size_t aRequestedBytes,
459 IResumable* aConsumer);
460 bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
461 size_t aBytes) const;
463 void OnIteratorRelease();
465 //////////////////////////////////////////////////////////////////////////////
466 // Helper methods.
467 //////////////////////////////////////////////////////////////////////////////
469 nsresult HandleError(nsresult aError) MOZ_REQUIRES(mMutex);
470 bool IsEmpty() MOZ_REQUIRES(mMutex);
471 bool IsLastChunk(uint32_t aChunk) MOZ_REQUIRES(mMutex);
473 //////////////////////////////////////////////////////////////////////////////
474 // Member variables.
475 //////////////////////////////////////////////////////////////////////////////
477 /// All private members are protected by mMutex.
478 mutable Mutex mMutex;
480 /// The data in this SourceBuffer, stored as a series of Chunks.
481 AutoTArray<Chunk, 1> mChunks MOZ_GUARDED_BY(mMutex);
483 /// Consumers which are waiting to be notified when new data is available.
484 nsTArray<RefPtr<IResumable>> mWaitingConsumers MOZ_GUARDED_BY(mMutex);
486 /// If present, marks this SourceBuffer complete with the given final status.
487 Maybe<nsresult> mStatus MOZ_GUARDED_BY(mMutex);
489 /// Count of active consumers.
490 uint32_t mConsumerCount MOZ_GUARDED_BY(mMutex);
492 /// True if compacting has been performed.
493 bool mCompacted MOZ_GUARDED_BY(mMutex);
496 } // namespace image
497 } // namespace mozilla
499 #endif // mozilla_image_sourcebuffer_h