From c186f77a8433f24a44a6291a5501465e9a1f0396 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Sun, 3 Apr 2016 19:25:21 +1000 Subject: [PATCH] Bug 1248861: P1. Add AlignedBuffer template class. r=cpearce Along with AlignedByteBuffer and AlignedFloatBuffer MozReview-Commit-ID: LmGc2JDBETi --HG-- extra : rebase_source : b1091188870f0cfbfebcacad940504b02bc7c1dc --- dom/media/MediaData.cpp | 1 - dom/media/MediaData.h | 231 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 229 insertions(+), 3 deletions(-) diff --git a/dom/media/MediaData.cpp b/dom/media/MediaData.cpp index 3ccba39e2f7e..262f9eb1a2f6 100644 --- a/dom/media/MediaData.cpp +++ b/dom/media/MediaData.cpp @@ -12,7 +12,6 @@ #endif #include "VideoUtils.h" #include "ImageContainer.h" -#include "mozilla/UniquePtrExtensions.h" #ifdef MOZ_WIDGET_GONK #include diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h index 9bf8c6793ca3..0326c5fce678 100644 --- a/dom/media/MediaData.h +++ b/dom/media/MediaData.h @@ -14,7 +14,10 @@ #include "SharedBuffer.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" #include "nsTArray.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/PodOperations.h" namespace mozilla { @@ -26,6 +29,231 @@ class ImageContainer; class MediaByteBuffer; class SharedTrackInfo; +// AlignedBuffer: +// Memory allocations are fallibles. Methods return a boolean indicating if +// memory allocations were successful. Return values should always be checked. +// AlignedBuffer::mData will be nullptr if no memory has been allocated or if +// an error occurred during construction. +// Existing data is only ever modified if new memory allocation has succeeded +// and preserved if not. +// +// The memory referenced by mData will always be Alignment bytes aligned and the +// underlying buffer will always have a size such that Alignment bytes blocks +// can be used to read the content, regardless of the mSize value. Buffer is +// zeroed on creation, elements are not individually constructed. +// An Alignment value of 0 means that the data isn't aligned. +// +// Type must be trivially copyable. +// +// AlignedBuffer can typically be used in place of UniquePtr however +// care must be taken as all memory allocations are fallible. +// Example: +// auto buffer = MakeUniqueFallible(samples) +// becomes: AlignedFloatBuffer buffer(samples) +// +// auto buffer = MakeUnique(samples) +// becomes: +// AlignedFloatBuffer buffer(samples); +// if (!buffer) { return NS_ERROR_OUT_OF_MEMORY; } + +template +class AlignedBuffer +{ +public: + AlignedBuffer() + : mData(nullptr) + , mLength(0) + , mBuffer(nullptr) + , mCapacity(0) + {} + + explicit AlignedBuffer(size_t aLength) + : mData(nullptr) + , mLength(0) + , mBuffer(nullptr) + , mCapacity(0) + { + if (EnsureCapacity(aLength)) { + mLength = aLength; + } + } + + AlignedBuffer(const Type* aData, size_t aLength) + : AlignedBuffer(aLength) + { + if (!mData) { + return; + } + PodCopy(mData, aData, aLength); + } + + AlignedBuffer(const AlignedBuffer& aOther) + : AlignedBuffer(aOther.Data(), aOther.Length()) + {} + + AlignedBuffer(AlignedBuffer&& aOther) + : mData(aOther.mData) + , mLength(aOther.mLength) + , mBuffer(Move(aOther.mBuffer)) + , mCapacity(aOther.mCapacity) + { + aOther.mData = nullptr; + aOther.mLength = 0; + aOther.mCapacity = 0; + } + + AlignedBuffer& operator=(AlignedBuffer&& aOther) + { + this->~AlignedBuffer(); + new (this) AlignedBuffer(Move(aOther)); + return *this; + } + + Type* Data() const { return mData; } + size_t Length() const { return mLength; } + size_t Size() const { return mLength * sizeof(Type); } + Type& operator[](size_t aIndex) + { + MOZ_ASSERT(aIndex < mLength); + return mData[aIndex]; + } + const Type& operator[](size_t aIndex) const + { + MOZ_ASSERT(aIndex < mLength); + return mData[aIndex]; + } + // Set length of buffer, allocating memory as required. + // If length is increased, new buffer area is filled with 0. + bool SetLength(size_t aLength) + { + if (aLength > mLength && !EnsureCapacity(aLength)) { + return false; + } + mLength = aLength; + return true; + } + // Add aData at the beginning of buffer. + bool Prepend(const Type* aData, size_t aLength) + { + if (!EnsureCapacity(aLength + mLength)) { + return false; + } + + // Shift the data to the right by aLength to leave room for the new data. + PodMove(mData + aLength, mData, mLength); + PodCopy(mData, aData, aLength); + + mLength += aLength; + return true; + } + // Add aData at the end of buffer. + bool Append(const Type* aData, size_t aLength) + { + if (!EnsureCapacity(aLength + mLength)) { + return false; + } + + PodCopy(mData + mLength, aData, aLength); + + mLength += aLength; + return true; + } + // Replace current content with aData. + bool Replace(const Type* aData, size_t aLength) + { + // If aLength is smaller than our current length, we leave the buffer as is, + // only adjusting the reported length. + if (!EnsureCapacity(aLength)) { + return false; + } + + PodCopy(mData, aData, aLength); + mLength = aLength; + return true; + } + // Clear the memory buffer. Will set target mData and mLength to 0. + void Clear() + { + mLength = 0; + mData = nullptr; + } + + // Methods for reporting memory. + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const + { + size_t size = aMallocSizeOf(this); + size += aMallocSizeOf(mBuffer.get()); + return size; + } + // AlignedBuffer is typically allocated on the stack. As such, you likely + // want to use SizeOfExcludingThis + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const + { + return aMallocSizeOf(mBuffer.get()); + } + size_t ComputedSizeOfExcludingThis() const + { + return mCapacity; + } + + // For backward compatibility with UniquePtr + Type* get() const { return mData; } + explicit operator bool() const { return mData != nullptr; } + +private: + size_t AlignmentOffset() const + { + return Alignment ? Alignment - 1 : 0; + } + + // Ensure that the backend buffer can hold aLength data. Will update mData. + // Will enforce that the start of allocated data is always Alignment bytes + // aligned and that it has sufficient end padding to allow for Alignment bytes + // block read as required by some data decoders. + // Returns false if memory couldn't be allocated. + bool EnsureCapacity(size_t aLength) + { + const CheckedInt sizeNeeded = + CheckedInt(aLength) * sizeof(Type) + AlignmentOffset() * 2; + + if (!sizeNeeded.isValid()) { + // overflow. + return false; + } + if (mData && mCapacity >= sizeNeeded.value()) { + return true; + } + auto newBuffer = MakeUniqueFallible(sizeNeeded.value()); + if (!newBuffer) { + return false; + } + + // Find alignment address. + const uintptr_t alignmask = AlignmentOffset(); + Type* newData = reinterpret_cast( + (reinterpret_cast(newBuffer.get()) + alignmask) & ~alignmask); + MOZ_ASSERT(uintptr_t(newData) % (AlignmentOffset()+1) == 0); + + PodZero(newData + mLength, aLength - mLength); + PodCopy(newData, mData, mLength); + + mBuffer = Move(newBuffer); + mCapacity = sizeNeeded.value(); + mData = newData; + + return true; + } + Type* mData; + size_t mLength; + UniquePtr mBuffer; + size_t mCapacity; +}; + +typedef AlignedBuffer AlignedByteBuffer; +typedef AlignedBuffer AlignedFloatBuffer; +typedef AlignedBuffer AlignedShortBuffer; +typedef AlignedBuffer AlignedAudioBuffer; + // Container that holds media samples. class MediaData { public: @@ -338,12 +566,11 @@ public: nsTArray mSessionIds; }; - // MediaRawData is a MediaData container used to store demuxed, still compressed // samples. // Use MediaRawData::CreateWriter() to obtain a MediaRawDataWriter object that // provides methods to modify and manipulate the data. -// Memory allocations are fallibles. Methods return a boolean indicating if +// Memory allocations are fallible. Methods return a boolean indicating if // memory allocations were successful. Return values should always be checked. // MediaRawData::mData will be nullptr if no memory has been allocated or if // an error occurred during construction.