SourceBuffer.h (18912B)
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 <utility> 16 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 "nsTArray.h" 23 24 class nsIInputStream; 25 26 namespace mozilla { 27 namespace image { 28 29 class SourceBuffer; 30 31 /** 32 * IResumable is an interface for classes that can schedule themselves to resume 33 * their work later. An implementation of IResumable generally should post a 34 * runnable to some event target which continues the work of the task. 35 */ 36 struct IResumable { 37 MOZ_DECLARE_REFCOUNTED_TYPENAME(IResumable) 38 39 // Subclasses may or may not be XPCOM classes, so we just require that they 40 // implement AddRef and Release. 41 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING 42 43 virtual void Resume() = 0; 44 45 protected: 46 virtual ~IResumable() {} 47 }; 48 49 /** 50 * SourceBufferIterator is a class that allows consumers of image source data to 51 * read the contents of a SourceBuffer sequentially. 52 * 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. 56 * 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. 60 * 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. 63 * 64 * Finally, in the COMPLETE state the consumer can call CompletionStatus() to 65 * get the status passed to SourceBuffer::Complete(). 66 */ 67 class SourceBufferIterator final { 68 public: 69 enum State { 70 START, // The iterator is at the beginning of the buffer. 71 READY, // The iterator is pointing to new data. 72 WAITING, // The iterator is blocked and the caller must yield. 73 COMPLETE // The iterator is pointing to the end of the buffer. 74 }; 75 76 explicit SourceBufferIterator(SourceBuffer* aOwner, size_t aReadLimit) 77 : mOwner(aOwner), 78 mState(START), 79 mChunkCount(0), 80 mByteCount(0), 81 mRemainderToRead(aReadLimit) { 82 MOZ_ASSERT(aOwner); 83 mData.mIterating.mChunk = 0; 84 mData.mIterating.mData = nullptr; 85 mData.mIterating.mOffset = 0; 86 mData.mIterating.mAvailableLength = 0; 87 mData.mIterating.mNextReadLength = 0; 88 } 89 90 SourceBufferIterator(SourceBufferIterator&& aOther) 91 : mOwner(std::move(aOther.mOwner)), 92 mState(aOther.mState), 93 mData(aOther.mData), 94 mChunkCount(aOther.mChunkCount), 95 mByteCount(aOther.mByteCount), 96 mRemainderToRead(aOther.mRemainderToRead) {} 97 98 ~SourceBufferIterator(); 99 100 SourceBufferIterator& operator=(SourceBufferIterator&& aOther); 101 102 /** 103 * Returns true if there are no more than @aBytes remaining in the 104 * SourceBuffer. If the SourceBuffer is not yet complete, returns false. 105 */ 106 bool RemainingBytesIsNoMoreThan(size_t aBytes) const; 107 108 /** 109 * Advances the iterator through the SourceBuffer if possible. Advances no 110 * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as 111 * possible.) 112 * 113 * This is a wrapper around AdvanceOrScheduleResume() that makes it clearer at 114 * the callsite when the no resuming is intended. 115 * 116 * @return State::READY if the iterator was successfully advanced. 117 * State::WAITING if the iterator could not be advanced because it's 118 * at the end of the underlying SourceBuffer, but the SourceBuffer 119 * may still receive additional data. 120 * State::COMPLETE if the iterator could not be advanced because it's 121 * at the end of the underlying SourceBuffer and the SourceBuffer is 122 * marked complete (i.e., it will never receive any additional 123 * data). 124 */ 125 State Advance(size_t aRequestedBytes) { 126 return AdvanceOrScheduleResume(aRequestedBytes, nullptr); 127 } 128 129 /** 130 * Advances the iterator through the SourceBuffer if possible. Advances no 131 * more than @aRequestedBytes bytes. (Use SIZE_MAX to advance as much as 132 * possible.) If advancing is not possible and @aConsumer is not null, 133 * arranges to call the @aConsumer's Resume() method when more data is 134 * available. 135 * 136 * @return State::READY if the iterator was successfully advanced. 137 * State::WAITING if the iterator could not be advanced because it's 138 * at the end of the underlying SourceBuffer, but the SourceBuffer 139 * may still receive additional data. @aConsumer's Resume() method 140 * will be called when additional data is available. 141 * State::COMPLETE if the iterator could not be advanced because it's 142 * at the end of the underlying SourceBuffer and the SourceBuffer is 143 * marked complete (i.e., it will never receive any additional 144 * data). 145 */ 146 State AdvanceOrScheduleResume(size_t aRequestedBytes, IResumable* aConsumer); 147 148 /// If at the end, returns the status passed to SourceBuffer::Complete(). 149 nsresult CompletionStatus() const { 150 MOZ_ASSERT(mState == COMPLETE, 151 "Calling CompletionStatus() in the wrong state"); 152 return mState == COMPLETE ? mData.mAtEnd.mStatus : NS_OK; 153 } 154 155 /// If we're ready to read, returns a pointer to the new data. 156 const char* Data() const { 157 MOZ_ASSERT(mState == READY, "Calling Data() in the wrong state"); 158 return mState == READY ? mData.mIterating.mData + mData.mIterating.mOffset 159 : nullptr; 160 } 161 162 /// If we're ready to read, returns the length of the new data. 163 size_t Length() const { 164 MOZ_ASSERT(mState == READY, "Calling Length() in the wrong state"); 165 return mState == READY ? mData.mIterating.mNextReadLength : 0; 166 } 167 168 /// If we're ready to read, returns whether or not everything available thus 169 /// far has been in the same contiguous buffer. 170 bool IsContiguous() const { 171 MOZ_ASSERT(mState == READY, "Calling IsContiguous() in the wrong state"); 172 return mState == READY ? mData.mIterating.mChunk == 0 : false; 173 } 174 175 /// @return a count of the chunks we've advanced through. 176 uint32_t ChunkCount() const { return mChunkCount; } 177 178 /// @return a count of the bytes in all chunks we've advanced through. 179 size_t ByteCount() const { return mByteCount; } 180 181 /// @return the source buffer which owns the iterator. 182 SourceBuffer* Owner() const { 183 MOZ_ASSERT(mOwner); 184 return mOwner; 185 } 186 187 /// @return the current offset from the beginning of the buffer. 188 size_t Position() const { 189 return mByteCount - mData.mIterating.mAvailableLength; 190 } 191 192 private: 193 friend class SourceBuffer; 194 195 SourceBufferIterator(const SourceBufferIterator&) = delete; 196 SourceBufferIterator& operator=(const SourceBufferIterator&) = delete; 197 198 bool HasMore() const { return mState != COMPLETE; } 199 200 State AdvanceFromLocalBuffer(size_t aRequestedBytes) { 201 MOZ_ASSERT(mState == READY, "Advancing in the wrong state"); 202 MOZ_ASSERT(mData.mIterating.mAvailableLength > 0, 203 "The local buffer shouldn't be empty"); 204 MOZ_ASSERT(mData.mIterating.mNextReadLength == 0, 205 "Advancing without consuming previous data"); 206 207 mData.mIterating.mNextReadLength = 208 std::min(mData.mIterating.mAvailableLength, aRequestedBytes); 209 210 return READY; 211 } 212 213 State SetReady(uint32_t aChunk, const char* aData, size_t aOffset, 214 size_t aAvailableLength, size_t aRequestedBytes) { 215 MOZ_ASSERT(mState != COMPLETE); 216 mState = READY; 217 218 // Prevent the iterator from reporting more data than it is allowed to read. 219 if (aAvailableLength > mRemainderToRead) { 220 aAvailableLength = mRemainderToRead; 221 } 222 223 // Update state. 224 mData.mIterating.mChunk = aChunk; 225 mData.mIterating.mData = aData; 226 mData.mIterating.mOffset = aOffset; 227 mData.mIterating.mAvailableLength = aAvailableLength; 228 229 // Update metrics. 230 mChunkCount++; 231 mByteCount += aAvailableLength; 232 233 // Attempt to advance by the requested number of bytes. 234 return AdvanceFromLocalBuffer(aRequestedBytes); 235 } 236 237 State SetWaiting(bool aHasConsumer) { 238 MOZ_ASSERT(mState != COMPLETE); 239 // Without a consumer, we won't know when to wake up precisely. Caller 240 // convention should mean that we don't try to advance unless we have 241 // written new data, but that doesn't mean we got enough. 242 MOZ_ASSERT(mState != WAITING || !aHasConsumer, 243 "Did we get a spurious wakeup somehow?"); 244 return mState = WAITING; 245 } 246 247 State SetComplete(nsresult aStatus) { 248 mData.mAtEnd.mStatus = aStatus; 249 return mState = COMPLETE; 250 } 251 252 RefPtr<SourceBuffer> mOwner; 253 254 State mState; 255 256 /** 257 * This union contains our iteration state if we're still iterating (for 258 * states START, READY, and WAITING) and the status the SourceBuffer was 259 * completed with if we're in state COMPLETE. 260 */ 261 union { 262 struct { 263 uint32_t mChunk; // Index of the chunk in SourceBuffer. 264 const char* mData; // Pointer to the start of the chunk. 265 size_t mOffset; // Current read position of the iterator relative to 266 // mData. 267 size_t mAvailableLength; // How many bytes remain unread in the chunk, 268 // relative to mOffset. 269 size_t 270 mNextReadLength; // How many bytes the last iterator advance 271 // requested to be read, so that we know much 272 // to increase mOffset and reduce mAvailableLength 273 // by when the next advance is requested. 274 } mIterating; // Cached info of the chunk currently iterating over. 275 struct { 276 nsresult mStatus; // Status code indicating if we read all the data. 277 } mAtEnd; // State info after iterator is complete. 278 } mData; 279 280 uint32_t mChunkCount; // Count of chunks observed, including current chunk. 281 size_t mByteCount; // Count of readable bytes observed, including unread 282 // bytes from the current chunk. 283 size_t mRemainderToRead; // Count of bytes left to read if there is a maximum 284 // imposed by the caller. SIZE_MAX if unlimited. 285 }; 286 287 /** 288 * SourceBuffer is a parallel data structure used for storing image source 289 * (compressed) data. 290 * 291 * SourceBuffer is a single producer, multiple consumer data structure. The 292 * single producer calls Append() to append data to the buffer. In parallel, 293 * multiple consumers can call Iterator(), which returns a SourceBufferIterator 294 * that they can use to iterate through the buffer. The SourceBufferIterator 295 * returns a series of pointers which remain stable for lifetime of the 296 * SourceBuffer, and the data they point to is immutable, ensuring that the 297 * producer never interferes with the consumers. 298 * 299 * In order to avoid blocking, SourceBuffer works with SourceBufferIterator to 300 * keep a list of consumers which are waiting for new data, and to resume them 301 * when the producer appends more. All consumers must implement the IResumable 302 * interface to make this possible. 303 */ 304 class SourceBuffer final { 305 public: 306 MOZ_DECLARE_REFCOUNTED_TYPENAME(image::SourceBuffer) 307 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(image::SourceBuffer) 308 309 SourceBuffer(); 310 311 ////////////////////////////////////////////////////////////////////////////// 312 // Producer methods. 313 ////////////////////////////////////////////////////////////////////////////// 314 315 /** 316 * If the producer knows how long the source data will be, it should call 317 * ExpectLength, which enables SourceBuffer to preallocate its buffer. 318 */ 319 nsresult ExpectLength(size_t aExpectedLength); 320 321 /// Append the provided data to the buffer. 322 nsresult Append(const char* aData, size_t aLength); 323 324 /// Append the data available on the provided nsIInputStream to the buffer. 325 nsresult AppendFromInputStream(nsIInputStream* aInputStream, uint32_t aCount); 326 327 /// Take ownership of provided data and append it without copying. 328 nsresult AdoptData(char* aData, size_t aLength, 329 void* (*aRealloc)(void*, size_t), void (*aFree)(void*)); 330 331 /** 332 * Mark the buffer complete, with a status that will be available to 333 * consumers. Further calls to Append() are forbidden after Complete(). 334 */ 335 void Complete(nsresult aStatus); 336 337 /// Returns true if the buffer is complete. 338 bool IsComplete(); 339 340 /// Memory reporting. 341 size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const; 342 343 ////////////////////////////////////////////////////////////////////////////// 344 // Consumer methods. 345 ////////////////////////////////////////////////////////////////////////////// 346 347 /** 348 * Returns an iterator to this SourceBuffer, which cannot read more than the 349 * given length. 350 */ 351 SourceBufferIterator Iterator(size_t aReadLength = SIZE_MAX); 352 353 ////////////////////////////////////////////////////////////////////////////// 354 // Consumer methods. 355 ////////////////////////////////////////////////////////////////////////////// 356 357 /** 358 * The minimum chunk capacity we'll allocate, if we don't know the correct 359 * capacity (which would happen because ExpectLength() wasn't called or gave 360 * us the wrong value). This is only exposed for use by tests; if normal code 361 * is using this, it's doing something wrong. 362 */ 363 static const size_t MIN_CHUNK_CAPACITY = 4096; 364 365 /** 366 * The maximum chunk capacity we'll allocate. This was historically the 367 * maximum we would preallocate based on the network size. We may adjust it 368 * in the future based on the IMAGE_DECODE_CHUNKS telemetry to ensure most 369 * images remain in a single chunk. 370 */ 371 static const size_t MAX_CHUNK_CAPACITY = 20 * 1024 * 1024; 372 373 private: 374 friend class SourceBufferIterator; 375 376 ~SourceBuffer(); 377 378 ////////////////////////////////////////////////////////////////////////////// 379 // Chunk type and chunk-related methods. 380 ////////////////////////////////////////////////////////////////////////////// 381 382 class Chunk final { 383 public: 384 explicit Chunk(size_t aCapacity) : mCapacity(aCapacity), mLength(0) { 385 MOZ_ASSERT(aCapacity > 0, "Creating zero-capacity chunk"); 386 mData = static_cast<char*>(malloc(mCapacity)); 387 } 388 389 /// Create an "adopted" chunk taking ownership of the provided data. 390 Chunk(char* aData, size_t aLength, void* (*aRealloc)(void*, size_t), 391 void (*aFree)(void*)) 392 : mCapacity(aLength), 393 mLength(aLength), 394 mData(aData), 395 mRealloc(aRealloc), 396 mFree(aFree) {} 397 398 ~Chunk() { mFree(mData); } 399 400 Chunk(Chunk&& aOther) 401 : mCapacity(aOther.mCapacity), 402 mLength(aOther.mLength), 403 mData(aOther.mData) { 404 aOther.mCapacity = aOther.mLength = 0; 405 aOther.mData = nullptr; 406 } 407 408 Chunk& operator=(Chunk&& aOther) { 409 free(mData); 410 mCapacity = aOther.mCapacity; 411 mLength = aOther.mLength; 412 mData = aOther.mData; 413 aOther.mCapacity = aOther.mLength = 0; 414 aOther.mData = nullptr; 415 return *this; 416 } 417 418 bool AllocationFailed() const { return !mData; } 419 size_t Capacity() const { return mCapacity; } 420 size_t Length() const { return mLength; } 421 422 char* Data() const { 423 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it"); 424 return mData; 425 } 426 427 void AddLength(size_t aAdditionalLength) { 428 MOZ_ASSERT(mLength + aAdditionalLength <= mCapacity); 429 mLength += aAdditionalLength; 430 } 431 432 bool SetCapacity(size_t aCapacity) { 433 MOZ_ASSERT(mData, "Allocation failed but nobody checked for it"); 434 char* data = static_cast<char*>(mRealloc(mData, aCapacity)); 435 if (!data) { 436 return false; 437 } 438 439 mData = data; 440 mCapacity = aCapacity; 441 return true; 442 } 443 444 private: 445 Chunk(const Chunk&) = delete; 446 Chunk& operator=(const Chunk&) = delete; 447 448 size_t mCapacity; 449 size_t mLength; 450 char* mData; 451 void* (*mRealloc)(void*, size_t) = realloc; 452 void (*mFree)(void*) = free; 453 }; 454 455 nsresult AppendChunk(Maybe<Chunk>&& aChunk) MOZ_REQUIRES(mMutex); 456 Maybe<Chunk> CreateChunk(size_t aCapacity, size_t aExistingCapacity = 0, 457 bool aRoundUp = true); 458 nsresult Compact() MOZ_REQUIRES(mMutex); 459 static size_t RoundedUpCapacity(size_t aCapacity); 460 size_t FibonacciCapacityWithMinimum(size_t aMinCapacity) MOZ_REQUIRES(mMutex); 461 462 ////////////////////////////////////////////////////////////////////////////// 463 // Iterator / consumer methods. 464 ////////////////////////////////////////////////////////////////////////////// 465 466 void AddWaitingConsumer(IResumable* aConsumer) MOZ_REQUIRES(mMutex); 467 void ResumeWaitingConsumers() MOZ_REQUIRES(mMutex); 468 469 typedef SourceBufferIterator::State State; 470 471 State AdvanceIteratorOrScheduleResume(SourceBufferIterator& aIterator, 472 size_t aRequestedBytes, 473 IResumable* aConsumer); 474 bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator, 475 size_t aBytes) const; 476 477 void OnIteratorRelease(); 478 479 ////////////////////////////////////////////////////////////////////////////// 480 // Helper methods. 481 ////////////////////////////////////////////////////////////////////////////// 482 483 nsresult HandleError(nsresult aError) MOZ_REQUIRES(mMutex); 484 bool IsEmpty() MOZ_REQUIRES(mMutex); 485 bool IsLastChunk(uint32_t aChunk) MOZ_REQUIRES(mMutex); 486 487 ////////////////////////////////////////////////////////////////////////////// 488 // Member variables. 489 ////////////////////////////////////////////////////////////////////////////// 490 491 /// All private members are protected by mMutex. 492 mutable Mutex mMutex; 493 494 /// The data in this SourceBuffer, stored as a series of Chunks. 495 AutoTArray<Chunk, 1> mChunks MOZ_GUARDED_BY(mMutex); 496 497 /// Consumers which are waiting to be notified when new data is available. 498 nsTArray<RefPtr<IResumable>> mWaitingConsumers MOZ_GUARDED_BY(mMutex); 499 500 /// If present, marks this SourceBuffer complete with the given final status. 501 Maybe<nsresult> mStatus MOZ_GUARDED_BY(mMutex); 502 503 /// Count of active consumers. 504 uint32_t mConsumerCount MOZ_GUARDED_BY(mMutex); 505 506 /// True if compacting has been performed. 507 bool mCompacted MOZ_GUARDED_BY(mMutex); 508 }; 509 510 } // namespace image 511 } // namespace mozilla 512 513 #endif // mozilla_image_sourcebuffer_h