Bug 1486801 - Clicking on the [...] should expand the markup container. r=jdescottes
[gecko.git] / image / SourceBuffer.h
blobff82cb2b9c5c8a8a59405ba10e9c492bb6a3c07a
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 "mozilla/Maybe.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/Mutex.h"
18 #include "mozilla/Move.h"
19 #include "mozilla/MemoryReporting.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/RefCounted.h"
22 #include "mozilla/UniquePtr.h"
23 #include "mozilla/RefPtr.h"
24 #include "nsTArray.h"
26 class nsIInputStream;
28 namespace mozilla {
29 namespace image {
31 class SourceBuffer;
33 /**
34 * IResumable is an interface for classes that can schedule themselves to resume
35 * their work later. An implementation of IResumable generally should post a
36 * runnable to some event target which continues the work of the task.
38 struct IResumable
40 MOZ_DECLARE_REFCOUNTED_TYPENAME(IResumable)
42 // Subclasses may or may not be XPCOM classes, so we just require that they
43 // implement AddRef and Release.
44 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
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, size_t aReadLimit)
81 : mOwner(aOwner)
82 , mState(START)
83 , mChunkCount(0)
84 , mByteCount(0)
85 , mRemainderToRead(aReadLimit)
87 MOZ_ASSERT(aOwner);
88 mData.mIterating.mChunk = 0;
89 mData.mIterating.mData = nullptr;
90 mData.mIterating.mOffset = 0;
91 mData.mIterating.mAvailableLength = 0;
92 mData.mIterating.mNextReadLength = 0;
95 SourceBufferIterator(SourceBufferIterator&& aOther)
96 : mOwner(std::move(aOther.mOwner))
97 , mState(aOther.mState)
98 , mData(aOther.mData)
99 , mChunkCount(aOther.mChunkCount)
100 , mByteCount(aOther.mByteCount)
101 , mRemainderToRead(aOther.mRemainderToRead)
104 ~SourceBufferIterator();
106 SourceBufferIterator& operator=(SourceBufferIterator&& aOther);
109 * Returns true if there are no more than @aBytes remaining in the
110 * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
112 bool RemainingBytesIsNoMoreThan(size_t aBytes) const;
115 * Advances the iterator through the SourceBuffer if possible. Advances no
116 * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
117 * possible.)
119 * This is a wrapper around AdvanceOrScheduleResume() that makes it clearer at
120 * the callsite when the no resuming is intended.
122 * @return State::READY if the iterator was successfully advanced.
123 * State::WAITING if the iterator could not be advanced because it's
124 * at the end of the underlying SourceBuffer, but the SourceBuffer
125 * may still receive additional data.
126 * State::COMPLETE if the iterator could not be advanced because it's
127 * at the end of the underlying SourceBuffer and the SourceBuffer is
128 * marked complete (i.e., it will never receive any additional
129 * data).
131 State Advance(size_t aRequestedBytes)
133 return AdvanceOrScheduleResume(aRequestedBytes, nullptr);
137 * Advances the iterator through the SourceBuffer if possible. Advances no
138 * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
139 * possible.) If advancing is not possible and @aConsumer is not null,
140 * arranges to call the @aConsumer's Resume() method when more data is
141 * available.
143 * @return State::READY if the iterator was successfully advanced.
144 * State::WAITING if the iterator could not be advanced because it's
145 * at the end of the underlying SourceBuffer, but the SourceBuffer
146 * may still receive additional data. @aConsumer's Resume() method
147 * will be called when additional data is available.
148 * State::COMPLETE if the iterator could not be advanced because it's
149 * at the end of the underlying SourceBuffer and the SourceBuffer is
150 * marked complete (i.e., it will never receive any additional
151 * data).
153 State AdvanceOrScheduleResume(size_t aRequestedBytes, IResumable* aConsumer);
155 /// If at the end, returns the status passed to SourceBuffer::Complete().
156 nsresult CompletionStatus() const
158 MOZ_ASSERT(mState == COMPLETE,
159 "Calling CompletionStatus() in the wrong state");
160 return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;
163 /// If we're ready to read, returns a pointer to the new data.
164 const char* Data() const
166 MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
167 return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
168 : nullptr;
171 /// If we're ready to read, returns the length of the new data.
172 size_t Length() const
174 MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
175 return mState == READY ? mData.mIterating.mNextReadLength : 0;
178 /// @return a count of the chunks we've advanced through.
179 uint32_t ChunkCount() const { return mChunkCount; }
181 /// @return a count of the bytes in all chunks we've advanced through.
182 size_t ByteCount() const { return mByteCount; }
184 /// @return the source buffer which owns the iterator.
185 SourceBuffer* Owner() const
187 MOZ_ASSERT(mOwner);
188 return mOwner;
191 /// @return the current offset from the beginning of the buffer.
192 size_t Position() const
194 return mByteCount - mData.mIterating.mAvailableLength;
197 private:
198 friend class SourceBuffer;
200 SourceBufferIterator(const SourceBufferIterator&) = delete;
201 SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
203 bool HasMore() const { return mState != COMPLETE; }
205 State AdvanceFromLocalBuffer(size_t aRequestedBytes)
207 MOZ_ASSERT(mState == READY, "Advancing in the wrong state");
208 MOZ_ASSERT(mData.mIterating.mAvailableLength > 0,
209 "The local buffer shouldn't be empty");
210 MOZ_ASSERT(mData.mIterating.mNextReadLength == 0,
211 "Advancing without consuming previous data");
213 mData.mIterating.mNextReadLength =
214 std::min(mData.mIterating.mAvailableLength, aRequestedBytes);
216 return READY;
219 State SetReady(uint32_t aChunk, const char* aData,
220 size_t aOffset, size_t aAvailableLength,
221 size_t aRequestedBytes)
223 MOZ_ASSERT(mState != COMPLETE);
224 mState = READY;
226 // Prevent the iterator from reporting more data than it is allowed to read.
227 if (aAvailableLength > mRemainderToRead) {
228 aAvailableLength = mRemainderToRead;
231 // Update state.
232 mData.mIterating.mChunk = aChunk;
233 mData.mIterating.mData = aData;
234 mData.mIterating.mOffset = aOffset;
235 mData.mIterating.mAvailableLength = aAvailableLength;
237 // Update metrics.
238 mChunkCount++;
239 mByteCount += aAvailableLength;
241 // Attempt to advance by the requested number of bytes.
242 return AdvanceFromLocalBuffer(aRequestedBytes);
245 State SetWaiting(bool aHasConsumer)
247 MOZ_ASSERT(mState != COMPLETE);
248 // Without a consumer, we won't know when to wake up precisely. Caller
249 // convention should mean that we don't try to advance unless we have
250 // written new data, but that doesn't mean we got enough.
251 MOZ_ASSERT(mState != WAITING || !aHasConsumer,
252 "Did we get a spurious wakeup somehow?");
253 return mState = WAITING;
256 State SetComplete(nsresult aStatus)
258 mData.mAtEnd.mStatus = aStatus;
259 return mState = COMPLETE;
262 RefPtr<SourceBuffer> mOwner;
264 State mState;
267 * This union contains our iteration state if we're still iterating (for
268 * states START, READY, and WAITING) and the status the SourceBuffer was
269 * completed with if we're in state COMPLETE.
271 union {
272 struct {
273 uint32_t mChunk; // Index of the chunk in SourceBuffer.
274 const char* mData; // Pointer to the start of the chunk.
275 size_t mOffset; // Current read position of the iterator relative to
276 // mData.
277 size_t mAvailableLength; // How many bytes remain unread in the chunk,
278 // relative to mOffset.
279 size_t mNextReadLength; // How many bytes the last iterator advance
280 // requested to be read, so that we know much
281 // to increase mOffset and reduce mAvailableLength
282 // by when the next advance is requested.
283 } mIterating; // Cached info of the chunk currently iterating over.
284 struct {
285 nsresult mStatus; // Status code indicating if we read all the data.
286 } mAtEnd; // State info after iterator is complete.
287 } mData;
289 uint32_t mChunkCount; // Count of chunks observed, including current chunk.
290 size_t mByteCount; // Count of readable bytes observed, including unread
291 // bytes from the current chunk.
292 size_t mRemainderToRead; // Count of bytes left to read if there is a maximum
293 // imposed by the caller. SIZE_MAX if unlimited.
297 * SourceBuffer is a parallel data structure used for storing image source
298 * (compressed) data.
300 * SourceBuffer is a single producer, multiple consumer data structure. The
301 * single producer calls Append() to append data to the buffer. In parallel,
302 * multiple consumers can call Iterator(), which returns a SourceBufferIterator
303 * that they can use to iterate through the buffer. The SourceBufferIterator
304 * returns a series of pointers which remain stable for lifetime of the
305 * SourceBuffer, and the data they point to is immutable, ensuring that the
306 * producer never interferes with the consumers.
308 * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to
309 * keep a list of consumers which are waiting for new data, and to resume them
310 * when the producer appends more. All consumers must implement the IResumable
311 * interface to make this possible.
313 class SourceBuffer final
315 public:
316 MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
317 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
319 SourceBuffer();
321 //////////////////////////////////////////////////////////////////////////////
322 // Producer methods.
323 //////////////////////////////////////////////////////////////////////////////
326 * If the producer knows how long the source data will be, it should call
327 * ExpectLength, which enables SourceBuffer to preallocate its buffer.
329 nsresult ExpectLength(size_t aExpectedLength);
331 /// Append the provided data to the buffer.
332 nsresult Append(const char* aData, size_t aLength);
334 /// Append the data available on the provided nsIInputStream to the buffer.
335 nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount);
338 * Mark the buffer complete, with a status that will be available to
339 * consumers. Further calls to Append() are forbidden after Complete().
341 void Complete(nsresult aStatus);
343 /// Returns true if the buffer is complete.
344 bool IsComplete();
346 /// Memory reporting.
347 size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
350 //////////////////////////////////////////////////////////////////////////////
351 // Consumer methods.
352 //////////////////////////////////////////////////////////////////////////////
355 * Returns an iterator to this SourceBuffer, which cannot read more than the
356 * given length.
358 SourceBufferIterator Iterator(size_t aReadLength = SIZE_MAX);
361 //////////////////////////////////////////////////////////////////////////////
362 // Consumer methods.
363 //////////////////////////////////////////////////////////////////////////////
366 * The minimum chunk capacity we'll allocate, if we don't know the correct
367 * capacity (which would happen because ExpectLength() wasn't called or gave
368 * us the wrong value). This is only exposed for use by tests; if normal code
369 * is using this, it's doing something wrong.
371 static const size_t MIN_CHUNK_CAPACITY = 4096;
374 * The maximum chunk capacity we'll allocate. This was historically the
375 * maximum we would preallocate based on the network size. We may adjust it
376 * in the future based on the IMAGE_DECODE_CHUNKS telemetry to ensure most
377 * images remain in a single chunk.
379 static const size_t MAX_CHUNK_CAPACITY = 20*1024*1024;
381 private:
382 friend class SourceBufferIterator;
384 ~SourceBuffer();
386 //////////////////////////////////////////////////////////////////////////////
387 // Chunk type and chunk-related methods.
388 //////////////////////////////////////////////////////////////////////////////
390 class Chunk final
392 public:
393 explicit Chunk(size_t aCapacity)
394 : mCapacity(aCapacity)
395 , mLength(0)
397 MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
398 mData = static_cast<char*>(malloc(mCapacity));
401 ~Chunk()
403 free(mData);
406 Chunk(Chunk&& aOther)
407 : mCapacity(aOther.mCapacity)
408 , mLength(aOther.mLength)
409 , mData(aOther.mData)
411 aOther.mCapacity = aOther.mLength = 0;
412 aOther.mData = nullptr;
415 Chunk& operator=(Chunk&& aOther)
417 free(mData);
418 mCapacity = aOther.mCapacity;
419 mLength = aOther.mLength;
420 mData = aOther.mData;
421 aOther.mCapacity = aOther.mLength = 0;
422 aOther.mData = nullptr;
423 return *this;
426 bool AllocationFailed() const { return !mData; }
427 size_t Capacity() const { return mCapacity; }
428 size_t Length() const { return mLength; }
430 char* Data() const
432 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
433 return mData;
436 void AddLength(size_t aAdditionalLength)
438 MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
439 mLength += aAdditionalLength;
442 bool SetCapacity(size_t aCapacity)
444 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
445 char* data = static_cast<char*>(realloc(mData, aCapacity));
446 if (!data) {
447 return false;
450 mData = data;
451 mCapacity = aCapacity;
452 return true;
455 private:
456 Chunk(const Chunk&) = delete;
457 Chunk& operator=(const Chunk&) = delete;
459 size_t mCapacity;
460 size_t mLength;
461 char* mData;
464 nsresult AppendChunk(Maybe<Chunk>&& aChunk);
465 Maybe<Chunk> CreateChunk(size_t aCapacity,
466 size_t aExistingCapacity = 0,
467 bool aRoundUp = true);
468 nsresult Compact();
469 static size_t RoundedUpCapacity(size_t aCapacity);
470 size_t FibonacciCapacityWithMinimum(size_t aMinCapacity);
473 //////////////////////////////////////////////////////////////////////////////
474 // Iterator / consumer methods.
475 //////////////////////////////////////////////////////////////////////////////
477 void AddWaitingConsumer(IResumable* aConsumer);
478 void ResumeWaitingConsumers();
480 typedef SourceBufferIterator::State State;
482 State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
483 size_t aRequestedBytes,
484 IResumable* aConsumer);
485 bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
486 size_t aBytes) const;
488 void OnIteratorRelease();
490 //////////////////////////////////////////////////////////////////////////////
491 // Helper methods.
492 //////////////////////////////////////////////////////////////////////////////
494 nsresult HandleError(nsresult aError);
495 bool IsEmpty();
496 bool IsLastChunk(uint32_t aChunk);
499 //////////////////////////////////////////////////////////////////////////////
500 // Member variables.
501 //////////////////////////////////////////////////////////////////////////////
503 /// All private members are protected by mMutex.
504 mutable Mutex mMutex;
506 /// The data in this SourceBuffer, stored as a series of Chunks.
507 AutoTArray<Chunk, 1> mChunks;
509 /// Consumers which are waiting to be notified when new data is available.
510 nsTArray<RefPtr<IResumable>> mWaitingConsumers;
512 /// If present, marks this SourceBuffer complete with the given final status.
513 Maybe<nsresult> mStatus;
515 /// Count of active consumers.
516 uint32_t mConsumerCount;
518 /// True if compacting has been performed.
519 bool mCompacted;
522 } // namespace image
523 } // namespace mozilla
525 #endif // mozilla_image_sourcebuffer_h