Line data Source code
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/. */
5 :
6 : /**
7 : * SourceBuffer is a single producer, multiple consumer data structure used for
8 : * storing image source (compressed) data.
9 : */
10 :
11 : #ifndef mozilla_image_sourcebuffer_h
12 : #define mozilla_image_sourcebuffer_h
13 :
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"
25 :
26 : class nsIInputStream;
27 :
28 : namespace mozilla {
29 : namespace image {
30 :
31 : class SourceBuffer;
32 :
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.
37 : */
38 33 : struct IResumable
39 : {
40 : MOZ_DECLARE_REFCOUNTED_TYPENAME(IResumable)
41 :
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
45 :
46 : virtual void Resume() = 0;
47 :
48 : protected:
49 19 : virtual ~IResumable() { }
50 : };
51 :
52 : /**
53 : * SourceBufferIterator is a class that allows consumers of image source data to
54 : * read the contents of a SourceBuffer sequentially.
55 : *
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.
59 : *
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.
63 : *
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.
66 : *
67 : * Finally, in the COMPLETE state the consumer can call CompletionStatus() to
68 : * get the status passed to SourceBuffer::Complete().
69 : */
70 : class SourceBufferIterator final
71 : {
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.
78 : };
79 :
80 33 : explicit SourceBufferIterator(SourceBuffer* aOwner)
81 33 : : mOwner(aOwner)
82 : , mState(START)
83 : , mChunkCount(0)
84 33 : , mByteCount(0)
85 : {
86 33 : MOZ_ASSERT(aOwner);
87 33 : mData.mIterating.mChunk = 0;
88 33 : mData.mIterating.mData = nullptr;
89 33 : mData.mIterating.mOffset = 0;
90 33 : mData.mIterating.mAvailableLength = 0;
91 33 : mData.mIterating.mNextReadLength = 0;
92 33 : }
93 :
94 33 : SourceBufferIterator(SourceBufferIterator&& aOther)
95 33 : : mOwner(Move(aOther.mOwner))
96 33 : , mState(aOther.mState)
97 : , mData(aOther.mData)
98 33 : , mChunkCount(aOther.mChunkCount)
99 99 : , mByteCount(aOther.mByteCount)
100 33 : { }
101 :
102 : ~SourceBufferIterator();
103 :
104 : SourceBufferIterator& operator=(SourceBufferIterator&& aOther);
105 :
106 : /**
107 : * Returns true if there are no more than @aBytes remaining in the
108 : * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
109 : */
110 : bool RemainingBytesIsNoMoreThan(size_t aBytes) const;
111 :
112 : /**
113 : * Advances the iterator through the SourceBuffer if possible. Advances no
114 : * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
115 : * possible.)
116 : *
117 : * This is a wrapper around AdvanceOrScheduleResume() that makes it clearer at
118 : * the callsite when the no resuming is intended.
119 : *
120 : * @return State::READY if the iterator was successfully advanced.
121 : * State::WAITING if the iterator could not be advanced because it's
122 : * at the end of the underlying SourceBuffer, but the SourceBuffer
123 : * may still receive additional data.
124 : * State::COMPLETE if the iterator could not be advanced because it's
125 : * at the end of the underlying SourceBuffer and the SourceBuffer is
126 : * marked complete (i.e., it will never receive any additional
127 : * data).
128 : */
129 : State Advance(size_t aRequestedBytes)
130 : {
131 : return AdvanceOrScheduleResume(aRequestedBytes, nullptr);
132 : }
133 :
134 : /**
135 : * Advances the iterator through the SourceBuffer if possible. Advances no
136 : * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as
137 : * possible.) If advancing is not possible and @aConsumer is not null,
138 : * arranges to call the @aConsumer's Resume() method when more data is
139 : * available.
140 : *
141 : * @return State::READY if the iterator was successfully advanced.
142 : * State::WAITING if the iterator could not be advanced because it's
143 : * at the end of the underlying SourceBuffer, but the SourceBuffer
144 : * may still receive additional data. @aConsumer's Resume() method
145 : * will be called when additional data is available.
146 : * State::COMPLETE if the iterator could not be advanced because it's
147 : * at the end of the underlying SourceBuffer and the SourceBuffer is
148 : * marked complete (i.e., it will never receive any additional
149 : * data).
150 : */
151 : State AdvanceOrScheduleResume(size_t aRequestedBytes, IResumable* aConsumer);
152 :
153 : /// If at the end, returns the status passed to SourceBuffer::Complete().
154 0 : nsresult CompletionStatus() const
155 : {
156 0 : MOZ_ASSERT(mState == COMPLETE,
157 : "Calling CompletionStatus() in the wrong state");
158 0 : return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;
159 : }
160 :
161 : /// If we're ready to read, returns a pointer to the new data.
162 145 : const char* Data() const
163 : {
164 145 : MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
165 145 : return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
166 145 : : nullptr;
167 : }
168 :
169 : /// If we're ready to read, returns the length of the new data.
170 315 : size_t Length() const
171 : {
172 315 : MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
173 315 : return mState == READY ? mData.mIterating.mNextReadLength : 0;
174 : }
175 :
176 : /// @return a count of the chunks we've advanced through.
177 33 : uint32_t ChunkCount() const { return mChunkCount; }
178 :
179 : /// @return a count of the bytes in all chunks we've advanced through.
180 33 : size_t ByteCount() const { return mByteCount; }
181 :
182 : private:
183 : friend class SourceBuffer;
184 :
185 : SourceBufferIterator(const SourceBufferIterator&) = delete;
186 : SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
187 :
188 93 : bool HasMore() const { return mState != COMPLETE; }
189 :
190 54 : State AdvanceFromLocalBuffer(size_t aRequestedBytes)
191 : {
192 54 : MOZ_ASSERT(mState == READY, "Advancing in the wrong state");
193 54 : MOZ_ASSERT(mData.mIterating.mAvailableLength > 0,
194 : "The local buffer shouldn't be empty");
195 54 : MOZ_ASSERT(mData.mIterating.mNextReadLength == 0,
196 : "Advancing without consuming previous data");
197 :
198 54 : mData.mIterating.mNextReadLength =
199 54 : std::min(mData.mIterating.mAvailableLength, aRequestedBytes);
200 :
201 54 : return READY;
202 : }
203 :
204 33 : State SetReady(uint32_t aChunk, const char* aData,
205 : size_t aOffset, size_t aAvailableLength,
206 : size_t aRequestedBytes)
207 : {
208 33 : MOZ_ASSERT(mState != COMPLETE);
209 33 : mState = READY;
210 :
211 : // Update state.
212 33 : mData.mIterating.mChunk = aChunk;
213 33 : mData.mIterating.mData = aData;
214 33 : mData.mIterating.mOffset = aOffset;
215 33 : mData.mIterating.mAvailableLength = aAvailableLength;
216 :
217 : // Update metrics.
218 33 : mChunkCount++;
219 33 : mByteCount += aAvailableLength;
220 :
221 : // Attempt to advance by the requested number of bytes.
222 33 : return AdvanceFromLocalBuffer(aRequestedBytes);
223 : }
224 :
225 0 : State SetWaiting()
226 : {
227 0 : MOZ_ASSERT(mState != COMPLETE);
228 0 : MOZ_ASSERT(mState != WAITING, "Did we get a spurious wakeup somehow?");
229 0 : return mState = WAITING;
230 : }
231 :
232 0 : State SetComplete(nsresult aStatus)
233 : {
234 0 : mData.mAtEnd.mStatus = aStatus;
235 0 : return mState = COMPLETE;
236 : }
237 :
238 : RefPtr<SourceBuffer> mOwner;
239 :
240 : State mState;
241 :
242 : /**
243 : * This union contains our iteration state if we're still iterating (for
244 : * states START, READY, and WAITING) and the status the SourceBuffer was
245 : * completed with if we're in state COMPLETE.
246 : */
247 : union {
248 : struct {
249 : uint32_t mChunk;
250 : const char* mData;
251 : size_t mOffset;
252 : size_t mAvailableLength;
253 : size_t mNextReadLength;
254 : } mIterating;
255 : struct {
256 : nsresult mStatus;
257 : } mAtEnd;
258 : } mData;
259 :
260 : uint32_t mChunkCount; // Count of chunks we've advanced through.
261 : size_t mByteCount; // Count of bytes in all chunks we've advanced through.
262 : };
263 :
264 : /**
265 : * SourceBuffer is a parallel data structure used for storing image source
266 : * (compressed) data.
267 : *
268 : * SourceBuffer is a single producer, multiple consumer data structure. The
269 : * single producer calls Append() to append data to the buffer. In parallel,
270 : * multiple consumers can call Iterator(), which returns a SourceBufferIterator
271 : * that they can use to iterate through the buffer. The SourceBufferIterator
272 : * returns a series of pointers which remain stable for lifetime of the
273 : * SourceBuffer, and the data they point to is immutable, ensuring that the
274 : * producer never interferes with the consumers.
275 : *
276 : * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to
277 : * keep a list of consumers which are waiting for new data, and to resume them
278 : * when the producer appends more. All consumers must implement the IResumable
279 : * interface to make this possible.
280 : */
281 : class SourceBuffer final
282 : {
283 : public:
284 : MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
285 88 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
286 :
287 : SourceBuffer();
288 :
289 : //////////////////////////////////////////////////////////////////////////////
290 : // Producer methods.
291 : //////////////////////////////////////////////////////////////////////////////
292 :
293 : /**
294 : * If the producer knows how long the source data will be, it should call
295 : * ExpectLength, which enables SourceBuffer to preallocate its buffer.
296 : */
297 : nsresult ExpectLength(size_t aExpectedLength);
298 :
299 : /// Append the provided data to the buffer.
300 : nsresult Append(const char* aData, size_t aLength);
301 :
302 : /// Append the data available on the provided nsIInputStream to the buffer.
303 : nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount);
304 :
305 : /**
306 : * Mark the buffer complete, with a status that will be available to
307 : * consumers. Further calls to Append() are forbidden after Complete().
308 : */
309 : void Complete(nsresult aStatus);
310 :
311 : /// Returns true if the buffer is complete.
312 : bool IsComplete();
313 :
314 : /// Memory reporting.
315 : size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
316 :
317 :
318 : //////////////////////////////////////////////////////////////////////////////
319 : // Consumer methods.
320 : //////////////////////////////////////////////////////////////////////////////
321 :
322 : /// Returns an iterator to this SourceBuffer.
323 : SourceBufferIterator Iterator();
324 :
325 :
326 : //////////////////////////////////////////////////////////////////////////////
327 : // Consumer methods.
328 : //////////////////////////////////////////////////////////////////////////////
329 :
330 : /**
331 : * The minimum chunk capacity we'll allocate, if we don't know the correct
332 : * capacity (which would happen because ExpectLength() wasn't called or gave
333 : * us the wrong value). This is only exposed for use by tests; if normal code
334 : * is using this, it's doing something wrong.
335 : */
336 : static const size_t MIN_CHUNK_CAPACITY = 4096;
337 :
338 : private:
339 : friend class SourceBufferIterator;
340 :
341 : ~SourceBuffer();
342 :
343 : //////////////////////////////////////////////////////////////////////////////
344 : // Chunk type and chunk-related methods.
345 : //////////////////////////////////////////////////////////////////////////////
346 :
347 95 : class Chunk
348 : {
349 : public:
350 38 : explicit Chunk(size_t aCapacity)
351 38 : : mCapacity(aCapacity)
352 38 : , mLength(0)
353 : {
354 38 : MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
355 76 : mData.reset(new (fallible) char[mCapacity]);
356 38 : }
357 :
358 76 : Chunk(Chunk&& aOther)
359 76 : : mCapacity(aOther.mCapacity)
360 76 : , mLength(aOther.mLength)
361 152 : , mData(Move(aOther.mData))
362 : {
363 76 : aOther.mCapacity = aOther.mLength = 0;
364 76 : aOther.mData = nullptr;
365 76 : }
366 :
367 0 : Chunk& operator=(Chunk&& aOther)
368 : {
369 0 : mCapacity = aOther.mCapacity;
370 0 : mLength = aOther.mLength;
371 0 : mData = Move(aOther.mData);
372 0 : aOther.mCapacity = aOther.mLength = 0;
373 0 : aOther.mData = nullptr;
374 0 : return *this;
375 : }
376 :
377 57 : bool AllocationFailed() const { return !mData; }
378 143 : size_t Capacity() const { return mCapacity; }
379 308 : size_t Length() const { return mLength; }
380 :
381 130 : char* Data() const
382 : {
383 130 : MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
384 130 : return mData.get();
385 : }
386 :
387 39 : void AddLength(size_t aAdditionalLength)
388 : {
389 39 : MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
390 39 : mLength += aAdditionalLength;
391 39 : }
392 :
393 : private:
394 : Chunk(const Chunk&) = delete;
395 : Chunk& operator=(const Chunk&) = delete;
396 :
397 : size_t mCapacity;
398 : size_t mLength;
399 : UniquePtr<char[]> mData;
400 : };
401 :
402 : nsresult AppendChunk(Maybe<Chunk>&& aChunk);
403 : Maybe<Chunk> CreateChunk(size_t aCapacity, bool aRoundUp = true);
404 : nsresult Compact();
405 : static size_t RoundedUpCapacity(size_t aCapacity);
406 : size_t FibonacciCapacityWithMinimum(size_t aMinCapacity);
407 :
408 :
409 : //////////////////////////////////////////////////////////////////////////////
410 : // Iterator / consumer methods.
411 : //////////////////////////////////////////////////////////////////////////////
412 :
413 : void AddWaitingConsumer(IResumable* aConsumer);
414 : void ResumeWaitingConsumers();
415 :
416 : typedef SourceBufferIterator::State State;
417 :
418 : State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
419 : size_t aRequestedBytes,
420 : IResumable* aConsumer);
421 : bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
422 : size_t aBytes) const;
423 :
424 : void OnIteratorRelease();
425 :
426 : //////////////////////////////////////////////////////////////////////////////
427 : // Helper methods.
428 : //////////////////////////////////////////////////////////////////////////////
429 :
430 : nsresult HandleError(nsresult aError);
431 : bool IsEmpty();
432 : bool IsLastChunk(uint32_t aChunk);
433 :
434 :
435 : //////////////////////////////////////////////////////////////////////////////
436 : // Member variables.
437 : //////////////////////////////////////////////////////////////////////////////
438 :
439 : /// All private members are protected by mMutex.
440 : mutable Mutex mMutex;
441 :
442 : /// The data in this SourceBuffer, stored as a series of Chunks.
443 : FallibleTArray<Chunk> mChunks;
444 :
445 : /// Consumers which are waiting to be notified when new data is available.
446 : nsTArray<RefPtr<IResumable>> mWaitingConsumers;
447 :
448 : /// If present, marks this SourceBuffer complete with the given final status.
449 : Maybe<nsresult> mStatus;
450 :
451 : /// Count of active consumers.
452 : uint32_t mConsumerCount;
453 : };
454 :
455 : } // namespace image
456 : } // namespace mozilla
457 :
458 : #endif // mozilla_image_sourcebuffer_h
|