Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/SourceBuffer.h
Line
Count
Source (jump to first uncovered line)
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
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
0
  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
  explicit SourceBufferIterator(SourceBuffer* aOwner, size_t aReadLimit)
81
    : mOwner(aOwner)
82
    , mState(START)
83
    , mChunkCount(0)
84
    , mByteCount(0)
85
    , mRemainderToRead(aReadLimit)
86
0
  {
87
0
    MOZ_ASSERT(aOwner);
88
0
    mData.mIterating.mChunk = 0;
89
0
    mData.mIterating.mData = nullptr;
90
0
    mData.mIterating.mOffset = 0;
91
0
    mData.mIterating.mAvailableLength = 0;
92
0
    mData.mIterating.mNextReadLength = 0;
93
0
  }
94
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)
102
0
  { }
103
104
  ~SourceBufferIterator();
105
106
  SourceBufferIterator& operator=(SourceBufferIterator&& aOther);
107
108
  /**
109
   * Returns true if there are no more than @aBytes remaining in the
110
   * SourceBuffer. If the SourceBuffer is not yet complete, returns false.
111
   */
112
  bool RemainingBytesIsNoMoreThan(size_t aBytes) const;
113
114
  /**
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.)
118
   *
119
   * This is a wrapper around AdvanceOrScheduleResume() that makes it clearer at
120
   * the callsite when the no resuming is intended.
121
   *
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).
130
   */
131
  State Advance(size_t aRequestedBytes)
132
0
  {
133
0
    return AdvanceOrScheduleResume(aRequestedBytes, nullptr);
134
0
  }
135
136
  /**
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.
142
   *
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).
152
   */
153
  State AdvanceOrScheduleResume(size_t aRequestedBytes, IResumable* aConsumer);
154
155
  /// If at the end, returns the status passed to SourceBuffer::Complete().
156
  nsresult CompletionStatus() const
157
0
  {
158
0
    MOZ_ASSERT(mState == COMPLETE,
159
0
               "Calling CompletionStatus() in the wrong state");
160
0
    return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK;
161
0
  }
162
163
  /// If we're ready to read, returns a pointer to the new data.
164
  const char* Data() const
165
0
  {
166
0
    MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state");
167
0
    return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset
168
0
                           : nullptr;
169
0
  }
170
171
  /// If we're ready to read, returns the length of the new data.
172
  size_t Length() const
173
0
  {
174
0
    MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state");
175
0
    return mState == READY ? mData.mIterating.mNextReadLength : 0;
176
0
  }
177
178
  /// @return a count of the chunks we've advanced through.
179
0
  uint32_t ChunkCount() const { return mChunkCount; }
180
181
  /// @return a count of the bytes in all chunks we've advanced through.
182
0
  size_t ByteCount() const { return mByteCount; }
183
184
  /// @return the source buffer which owns the iterator.
185
  SourceBuffer* Owner() const
186
0
  {
187
0
    MOZ_ASSERT(mOwner);
188
0
    return mOwner;
189
0
  }
190
191
  /// @return the current offset from the beginning of the buffer.
192
  size_t Position() const
193
0
  {
194
0
    return mByteCount - mData.mIterating.mAvailableLength;
195
0
  }
196
197
private:
198
  friend class SourceBuffer;
199
200
  SourceBufferIterator(const SourceBufferIterator&) = delete;
201
  SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
202
203
0
  bool HasMore() const { return mState != COMPLETE; }
204
205
  State AdvanceFromLocalBuffer(size_t aRequestedBytes)
206
0
  {
207
0
    MOZ_ASSERT(mState == READY, "Advancing in the wrong state");
208
0
    MOZ_ASSERT(mData.mIterating.mAvailableLength > 0,
209
0
               "The local buffer shouldn't be empty");
210
0
    MOZ_ASSERT(mData.mIterating.mNextReadLength == 0,
211
0
               "Advancing without consuming previous data");
212
0
213
0
    mData.mIterating.mNextReadLength =
214
0
      std::min(mData.mIterating.mAvailableLength, aRequestedBytes);
215
0
216
0
    return READY;
217
0
  }
218
219
  State SetReady(uint32_t aChunk, const char* aData,
220
                 size_t aOffset, size_t aAvailableLength,
221
                 size_t aRequestedBytes)
222
0
  {
223
0
    MOZ_ASSERT(mState != COMPLETE);
224
0
    mState = READY;
225
0
226
0
    // Prevent the iterator from reporting more data than it is allowed to read.
227
0
    if (aAvailableLength > mRemainderToRead) {
228
0
      aAvailableLength = mRemainderToRead;
229
0
    }
230
0
231
0
    // Update state.
232
0
    mData.mIterating.mChunk = aChunk;
233
0
    mData.mIterating.mData = aData;
234
0
    mData.mIterating.mOffset = aOffset;
235
0
    mData.mIterating.mAvailableLength = aAvailableLength;
236
0
237
0
    // Update metrics.
238
0
    mChunkCount++;
239
0
    mByteCount += aAvailableLength;
240
0
241
0
    // Attempt to advance by the requested number of bytes.
242
0
    return AdvanceFromLocalBuffer(aRequestedBytes);
243
0
  }
244
245
  State SetWaiting(bool aHasConsumer)
246
0
  {
247
0
    MOZ_ASSERT(mState != COMPLETE);
248
0
    // Without a consumer, we won't know when to wake up precisely. Caller
249
0
    // convention should mean that we don't try to advance unless we have
250
0
    // written new data, but that doesn't mean we got enough.
251
0
    MOZ_ASSERT(mState != WAITING || !aHasConsumer,
252
0
               "Did we get a spurious wakeup somehow?");
253
0
    return mState = WAITING;
254
0
  }
255
256
  State SetComplete(nsresult aStatus)
257
0
  {
258
0
    mData.mAtEnd.mStatus = aStatus;
259
0
    return mState = COMPLETE;
260
0
  }
261
262
  RefPtr<SourceBuffer> mOwner;
263
264
  State mState;
265
266
  /**
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.
270
   */
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;
288
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.
294
};
295
296
/**
297
 * SourceBuffer is a parallel data structure used for storing image source
298
 * (compressed) data.
299
 *
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.
307
 *
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.
312
 */
313
class SourceBuffer final
314
{
315
public:
316
  MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer)
317
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer)
318
319
  SourceBuffer();
320
321
  //////////////////////////////////////////////////////////////////////////////
322
  // Producer methods.
323
  //////////////////////////////////////////////////////////////////////////////
324
325
  /**
326
   * If the producer knows how long the source data will be, it should call
327
   * ExpectLength, which enables SourceBuffer to preallocate its buffer.
328
   */
329
  nsresult ExpectLength(size_t aExpectedLength);
330
331
  /// Append the provided data to the buffer.
332
  nsresult Append(const char* aData, size_t aLength);
333
334
  /// Append the data available on the provided nsIInputStream to the buffer.
335
  nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount);
336
337
  /**
338
   * Mark the buffer complete, with a status that will be available to
339
   * consumers. Further calls to Append() are forbidden after Complete().
340
   */
341
  void Complete(nsresult aStatus);
342
343
  /// Returns true if the buffer is complete.
344
  bool IsComplete();
345
346
  /// Memory reporting.
347
  size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
348
349
350
  //////////////////////////////////////////////////////////////////////////////
351
  // Consumer methods.
352
  //////////////////////////////////////////////////////////////////////////////
353
354
  /**
355
   * Returns an iterator to this SourceBuffer, which cannot read more than the
356
   * given length.
357
   */
358
  SourceBufferIterator Iterator(size_t aReadLength = SIZE_MAX);
359
360
361
  //////////////////////////////////////////////////////////////////////////////
362
  // Consumer methods.
363
  //////////////////////////////////////////////////////////////////////////////
364
365
  /**
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.
370
   */
371
  static const size_t MIN_CHUNK_CAPACITY = 4096;
372
373
  /**
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.
378
   */
379
  static const size_t MAX_CHUNK_CAPACITY = 20*1024*1024;
380
381
private:
382
  friend class SourceBufferIterator;
383
384
  ~SourceBuffer();
385
386
  //////////////////////////////////////////////////////////////////////////////
387
  // Chunk type and chunk-related methods.
388
  //////////////////////////////////////////////////////////////////////////////
389
390
  class Chunk final
391
  {
392
  public:
393
    explicit Chunk(size_t aCapacity)
394
      : mCapacity(aCapacity)
395
      , mLength(0)
396
0
    {
397
0
      MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk");
398
0
      mData = static_cast<char*>(malloc(mCapacity));
399
0
    }
400
401
    ~Chunk()
402
0
    {
403
0
      free(mData);
404
0
    }
405
406
    Chunk(Chunk&& aOther)
407
      : mCapacity(aOther.mCapacity)
408
      , mLength(aOther.mLength)
409
      , mData(aOther.mData)
410
0
    {
411
0
      aOther.mCapacity = aOther.mLength = 0;
412
0
      aOther.mData = nullptr;
413
0
    }
414
415
    Chunk& operator=(Chunk&& aOther)
416
0
    {
417
0
      free(mData);
418
0
      mCapacity = aOther.mCapacity;
419
0
      mLength = aOther.mLength;
420
0
      mData = aOther.mData;
421
0
      aOther.mCapacity = aOther.mLength = 0;
422
0
      aOther.mData = nullptr;
423
0
      return *this;
424
0
    }
425
426
0
    bool AllocationFailed() const { return !mData; }
427
0
    size_t Capacity() const { return mCapacity; }
428
0
    size_t Length() const { return mLength; }
429
430
    char* Data() const
431
0
    {
432
0
      MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
433
0
      return mData;
434
0
    }
435
436
    void AddLength(size_t aAdditionalLength)
437
0
    {
438
0
      MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity);
439
0
      mLength += aAdditionalLength;
440
0
    }
441
442
    bool SetCapacity(size_t aCapacity)
443
0
    {
444
0
      MOZ_ASSERT(mData, "Allocation failed but nobody checked for it");
445
0
      char* data = static_cast<char*>(realloc(mData, aCapacity));
446
0
      if (!data) {
447
0
        return false;
448
0
      }
449
0
450
0
      mData = data;
451
0
      mCapacity = aCapacity;
452
0
      return true;
453
0
    }
454
455
  private:
456
    Chunk(const Chunk&) = delete;
457
    Chunk& operator=(const Chunk&) = delete;
458
459
    size_t mCapacity;
460
    size_t mLength;
461
    char* mData;
462
  };
463
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);
471
472
473
  //////////////////////////////////////////////////////////////////////////////
474
  // Iterator / consumer methods.
475
  //////////////////////////////////////////////////////////////////////////////
476
477
  void AddWaitingConsumer(IResumable* aConsumer);
478
  void ResumeWaitingConsumers();
479
480
  typedef SourceBufferIterator::State State;
481
482
  State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator,
483
                                        size_t aRequestedBytes,
484
                                        IResumable* aConsumer);
485
  bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
486
                                  size_t aBytes) const;
487
488
  void OnIteratorRelease();
489
490
  //////////////////////////////////////////////////////////////////////////////
491
  // Helper methods.
492
  //////////////////////////////////////////////////////////////////////////////
493
494
  nsresult HandleError(nsresult aError);
495
  bool IsEmpty();
496
  bool IsLastChunk(uint32_t aChunk);
497
498
499
  //////////////////////////////////////////////////////////////////////////////
500
  // Member variables.
501
  //////////////////////////////////////////////////////////////////////////////
502
503
  /// All private members are protected by mMutex.
504
  mutable Mutex mMutex;
505
506
  /// The data in this SourceBuffer, stored as a series of Chunks.
507
  AutoTArray<Chunk, 1> mChunks;
508
509
  /// Consumers which are waiting to be notified when new data is available.
510
  nsTArray<RefPtr<IResumable>> mWaitingConsumers;
511
512
  /// If present, marks this SourceBuffer complete with the given final status.
513
  Maybe<nsresult> mStatus;
514
515
  /// Count of active consumers.
516
  uint32_t mConsumerCount;
517
518
  /// True if compacting has been performed.
519
  bool mCompacted;
520
};
521
522
} // namespace image
523
} // namespace mozilla
524
525
#endif // mozilla_image_sourcebuffer_h