зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1315554 - Part 5. Add method to clone a SourceBufferIterator when decoding. r=tnikkel
This commit is contained in:
Родитель
10a558df00
Коммит
1404a847cc
|
@ -42,6 +42,7 @@ SourceBufferIterator::operator=(SourceBufferIterator&& aOther)
|
|||
mData = aOther.mData;
|
||||
mChunkCount = aOther.mChunkCount;
|
||||
mByteCount = aOther.mByteCount;
|
||||
mRemainderToRead = aOther.mRemainderToRead;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
@ -63,6 +64,25 @@ SourceBufferIterator::AdvanceOrScheduleResume(size_t aRequestedBytes,
|
|||
MOZ_ASSERT(mData.mIterating.mNextReadLength <= mData.mIterating.mAvailableLength);
|
||||
mData.mIterating.mOffset += mData.mIterating.mNextReadLength;
|
||||
mData.mIterating.mAvailableLength -= mData.mIterating.mNextReadLength;
|
||||
|
||||
// An iterator can have a limit imposed on it to read only a subset of a
|
||||
// source buffer. If it is present, we need to mimic the same behaviour as
|
||||
// the owning SourceBuffer.
|
||||
if (MOZ_UNLIKELY(mRemainderToRead != SIZE_MAX)) {
|
||||
MOZ_ASSERT(mData.mIterating.mNextReadLength <= mRemainderToRead);
|
||||
mRemainderToRead -= mData.mIterating.mNextReadLength;
|
||||
|
||||
if (MOZ_UNLIKELY(mRemainderToRead == 0)) {
|
||||
mData.mIterating.mNextReadLength = 0;
|
||||
SetComplete(NS_OK);
|
||||
return COMPLETE;
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(aRequestedBytes > mRemainderToRead)) {
|
||||
aRequestedBytes = mRemainderToRead;
|
||||
}
|
||||
}
|
||||
|
||||
mData.mIterating.mNextReadLength = 0;
|
||||
|
||||
if (MOZ_LIKELY(mState == READY)) {
|
||||
|
@ -518,14 +538,14 @@ SourceBuffer::SizeOfIncludingThisWithComputedFallback(MallocSizeOf
|
|||
}
|
||||
|
||||
SourceBufferIterator
|
||||
SourceBuffer::Iterator()
|
||||
SourceBuffer::Iterator(size_t aReadLength)
|
||||
{
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mConsumerCount++;
|
||||
}
|
||||
|
||||
return SourceBufferIterator(this);
|
||||
return SourceBufferIterator(this, aReadLength);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -77,11 +77,12 @@ public:
|
|||
COMPLETE // The iterator is pointing to the end of the buffer.
|
||||
};
|
||||
|
||||
explicit SourceBufferIterator(SourceBuffer* aOwner)
|
||||
explicit SourceBufferIterator(SourceBuffer* aOwner, size_t aReadLimit)
|
||||
: mOwner(aOwner)
|
||||
, mState(START)
|
||||
, mChunkCount(0)
|
||||
, mByteCount(0)
|
||||
, mRemainderToRead(aReadLimit)
|
||||
{
|
||||
MOZ_ASSERT(aOwner);
|
||||
mData.mIterating.mChunk = 0;
|
||||
|
@ -97,6 +98,7 @@ public:
|
|||
, mData(aOther.mData)
|
||||
, mChunkCount(aOther.mChunkCount)
|
||||
, mByteCount(aOther.mByteCount)
|
||||
, mRemainderToRead(aOther.mRemainderToRead)
|
||||
{ }
|
||||
|
||||
~SourceBufferIterator();
|
||||
|
@ -179,6 +181,19 @@ public:
|
|||
/// @return a count of the bytes in all chunks we've advanced through.
|
||||
size_t ByteCount() const { return mByteCount; }
|
||||
|
||||
/// @return the source buffer which owns the iterator.
|
||||
SourceBuffer* Owner() const
|
||||
{
|
||||
MOZ_ASSERT(mOwner);
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
/// @return the current offset from the beginning of the buffer.
|
||||
size_t Position() const
|
||||
{
|
||||
return mByteCount - mData.mIterating.mAvailableLength;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SourceBuffer;
|
||||
|
||||
|
@ -208,6 +223,11 @@ private:
|
|||
MOZ_ASSERT(mState != COMPLETE);
|
||||
mState = READY;
|
||||
|
||||
// Prevent the iterator from reporting more data than it is allowed to read.
|
||||
if (aAvailableLength > mRemainderToRead) {
|
||||
aAvailableLength = mRemainderToRead;
|
||||
}
|
||||
|
||||
// Update state.
|
||||
mData.mIterating.mChunk = aChunk;
|
||||
mData.mIterating.mData = aData;
|
||||
|
@ -246,19 +266,27 @@ private:
|
|||
*/
|
||||
union {
|
||||
struct {
|
||||
uint32_t mChunk;
|
||||
const char* mData;
|
||||
size_t mOffset;
|
||||
size_t mAvailableLength;
|
||||
size_t mNextReadLength;
|
||||
} mIterating;
|
||||
uint32_t mChunk; // Index of the chunk in SourceBuffer.
|
||||
const char* mData; // Pointer to the start of the chunk.
|
||||
size_t mOffset; // Current read position of the iterator relative to
|
||||
// mData.
|
||||
size_t mAvailableLength; // How many bytes remain unread in the chunk,
|
||||
// relative to mOffset.
|
||||
size_t mNextReadLength; // How many bytes the last iterator advance
|
||||
// requested to be read, so that we know much
|
||||
// to increase mOffset and reduce mAvailableLength
|
||||
// by when the next advance is requested.
|
||||
} mIterating; // Cached info of the chunk currently iterating over.
|
||||
struct {
|
||||
nsresult mStatus;
|
||||
} mAtEnd;
|
||||
nsresult mStatus; // Status code indicating if we read all the data.
|
||||
} mAtEnd; // State info after iterator is complete.
|
||||
} mData;
|
||||
|
||||
uint32_t mChunkCount; // Count of chunks we've advanced through.
|
||||
size_t mByteCount; // Count of bytes in all chunks we've advanced through.
|
||||
uint32_t mChunkCount; // Count of chunks observed, including current chunk.
|
||||
size_t mByteCount; // Count of readable bytes observed, including unread
|
||||
// bytes from the current chunk.
|
||||
size_t mRemainderToRead; // Count of bytes left to read if there is a maximum
|
||||
// imposed by the caller. SIZE_MAX if unlimited.
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -319,8 +347,11 @@ public:
|
|||
// Consumer methods.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Returns an iterator to this SourceBuffer.
|
||||
SourceBufferIterator Iterator();
|
||||
/**
|
||||
* Returns an iterator to this SourceBuffer, which cannot read more than the
|
||||
* given length.
|
||||
*/
|
||||
SourceBufferIterator Iterator(size_t aReadLength = SIZE_MAX);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -393,6 +393,52 @@ public:
|
|||
SetTransition(aStartState);
|
||||
}
|
||||
|
||||
/**
|
||||
* From the given SourceBufferIterator, aIterator, create a new iterator at
|
||||
* the same position, with the given read limit, aReadLimit. The read limit
|
||||
* applies after adjusting for the position. If the given iterator has been
|
||||
* advanced, but required buffering inside StreamingLexer, the position
|
||||
* of the cloned iterator will be at the beginning of buffered data; this
|
||||
* should match the perspective of the caller.
|
||||
*/
|
||||
SourceBufferIterator Clone(SourceBufferIterator& aIterator,
|
||||
size_t aReadLimit) const
|
||||
{
|
||||
// In order to advance to the current position of the iterator from the
|
||||
// perspective of the caller, we need to take into account if we are
|
||||
// buffering data.
|
||||
size_t pos = aIterator.Position();
|
||||
if (!mBuffer.empty()) {
|
||||
pos += aIterator.Length();
|
||||
MOZ_ASSERT(pos > mBuffer.length());
|
||||
pos -= mBuffer.length();
|
||||
}
|
||||
|
||||
size_t readLimit = aReadLimit;
|
||||
if (aReadLimit != SIZE_MAX) {
|
||||
readLimit += pos;
|
||||
}
|
||||
|
||||
SourceBufferIterator other = aIterator.Owner()->Iterator(readLimit);
|
||||
|
||||
// Since the current iterator has already advanced to this point, we
|
||||
// know that the state can only be READY or COMPLETE. That does not mean
|
||||
// everything is stored in a single chunk, and may require multiple Advance
|
||||
// calls to get where we want to be.
|
||||
DebugOnly<SourceBufferIterator::State> state;
|
||||
do {
|
||||
state = other.Advance(pos);
|
||||
MOZ_ASSERT(state != SourceBufferIterator::WAITING);
|
||||
MOZ_ASSERT(pos >= other.Length());
|
||||
pos -= other.Length();
|
||||
} while (pos > 0);
|
||||
|
||||
// Force the data pointer to be where we expect it to be.
|
||||
state = other.Advance(0);
|
||||
MOZ_ASSERT(state != SourceBufferIterator::WAITING);
|
||||
return other;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
LexerResult Lex(SourceBufferIterator& aIterator,
|
||||
IResumable* aOnResume,
|
||||
|
|
|
@ -808,3 +808,55 @@ TEST_F(ImageSourceBuffer, ExpectLengthDoesNotTriggerResume)
|
|||
// ensure that the test fails.
|
||||
mSourceBuffer->ExpectLength(1000);
|
||||
}
|
||||
|
||||
TEST_F(ImageSourceBuffer, CompleteSuccessWithSameReadLength)
|
||||
{
|
||||
SourceBufferIterator iterator = mSourceBuffer->Iterator(1);
|
||||
|
||||
// Write a single byte to the buffer and complete the buffer. (We have to
|
||||
// write at least one byte because completing a zero length buffer always
|
||||
// fails; see the ZeroLengthBufferAlwaysFails test.)
|
||||
CheckedAppendToBuffer(mData, 1);
|
||||
CheckedCompleteBuffer(iterator, 1);
|
||||
|
||||
// We should be able to advance once (to read the single byte) and then should
|
||||
// reach the COMPLETE state with a successful status.
|
||||
CheckedAdvanceIterator(iterator, 1);
|
||||
CheckIteratorIsComplete(iterator, 1);
|
||||
}
|
||||
|
||||
TEST_F(ImageSourceBuffer, CompleteSuccessWithSmallerReadLength)
|
||||
{
|
||||
// Create an iterator limited to one byte.
|
||||
SourceBufferIterator iterator = mSourceBuffer->Iterator(1);
|
||||
|
||||
// Write two bytes to the buffer and complete the buffer. (We have to
|
||||
// write at least one byte because completing a zero length buffer always
|
||||
// fails; see the ZeroLengthBufferAlwaysFails test.)
|
||||
CheckedAppendToBuffer(mData, 2);
|
||||
CheckedCompleteBuffer(iterator, 2);
|
||||
|
||||
// We should be able to advance once (to read the single byte) and then should
|
||||
// reach the COMPLETE state with a successful status, because our iterator is
|
||||
// limited to a single byte, rather than the full length.
|
||||
CheckedAdvanceIterator(iterator, 1);
|
||||
CheckIteratorIsComplete(iterator, 1);
|
||||
}
|
||||
|
||||
TEST_F(ImageSourceBuffer, CompleteSuccessWithGreaterReadLength)
|
||||
{
|
||||
// Create an iterator limited to one byte.
|
||||
SourceBufferIterator iterator = mSourceBuffer->Iterator(2);
|
||||
|
||||
// Write a single byte to the buffer and complete the buffer. (We have to
|
||||
// write at least one byte because completing a zero length buffer always
|
||||
// fails; see the ZeroLengthBufferAlwaysFails test.)
|
||||
CheckedAppendToBuffer(mData, 1);
|
||||
CheckedCompleteBuffer(iterator, 1);
|
||||
|
||||
// We should be able to advance once (to read the single byte) and then should
|
||||
// reach the COMPLETE state with a successful status. Our iterator lets us
|
||||
// read more but the underlying buffer has been completed.
|
||||
CheckedAdvanceIterator(iterator, 1);
|
||||
CheckIteratorIsComplete(iterator, 1);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче