--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -757,24 +757,65 @@ static int32_t GetMaxBlocks()
// but the latter formula avoids a potential overflow from `* 1024`.
// And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
// INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
constexpr uint32_t blockSizeKb = uint32_t(MediaCache::BLOCK_SIZE / 1024);
const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
return std::max(maxBlocks, int32_t(1));
}
+// Allowed range is whatever can be accessed with an int32_t block index.
+static bool
+IsOffsetAllowed(int64_t aOffset)
+{
+ return aOffset < (int64_t(INT32_MAX) + 1) * MediaCache::BLOCK_SIZE &&
+ aOffset >= 0;
+}
+
+// Convert 64-bit offset to 32-bit block index.
+// Assumes offset range-check was already done.
+static int32_t
+OffsetToBlockIndexUnchecked(int64_t aOffset)
+{
+ // Still check for allowed range in debug builds, to catch out-of-range
+ // issues early during development.
+ MOZ_ASSERT(IsOffsetAllowed(aOffset));
+ return int32_t(aOffset / MediaCache::BLOCK_SIZE);
+}
+
+// Convert 64-bit offset to 32-bit block index. -1 if out of allowed range.
+static int32_t
+OffsetToBlockIndex(int64_t aOffset)
+{
+ return IsOffsetAllowed(aOffset) ? OffsetToBlockIndexUnchecked(aOffset) : -1;
+}
+
+// Convert 64-bit offset to 32-bit offset inside a block.
+// Will not fail (even if offset is outside allowed range), so there is no
+// need to check for errors.
+static int32_t
+OffsetInBlock(int64_t aOffset)
+{
+ // Still check for allowed range in debug builds, to catch out-of-range
+ // issues early during development.
+ MOZ_ASSERT(IsOffsetAllowed(aOffset));
+ return int32_t(aOffset % MediaCache::BLOCK_SIZE);
+}
+
int32_t
MediaCache::FindBlockForIncomingData(TimeStamp aNow,
MediaCacheStream* aStream)
{
mReentrantMonitor.AssertCurrentThreadIn();
- int32_t blockIndex = FindReusableBlock(aNow, aStream,
- aStream->mChannelOffset/BLOCK_SIZE, INT32_MAX);
+ int32_t blockIndex =
+ FindReusableBlock(aNow,
+ aStream,
+ OffsetToBlockIndexUnchecked(aStream->mChannelOffset),
+ INT32_MAX);
if (blockIndex < 0 || !IsBlockFree(blockIndex)) {
// The block returned is already allocated.
// Don't reuse it if a) there's room to expand the cache or
// b) the data we're going to store in the free block is not higher
// priority than the data already stored in the free block.
// The latter can lead us to go over the cache limit a bit.
if ((mIndex.Length() < uint32_t(GetMaxBlocks()) || blockIndex < 0 ||
@@ -793,17 +834,18 @@ MediaCache::FindBlockForIncomingData(Tim
bool
MediaCache::BlockIsReusable(int32_t aBlockIndex)
{
Block* block = &mIndex[aBlockIndex];
for (uint32_t i = 0; i < block->mOwners.Length(); ++i) {
MediaCacheStream* stream = block->mOwners[i].mStream;
if (stream->mPinCount > 0 ||
- stream->mStreamOffset/BLOCK_SIZE == block->mOwners[i].mStreamBlock) {
+ uint32_t(OffsetToBlockIndex(stream->mStreamOffset)) ==
+ block->mOwners[i].mStreamBlock) {
return false;
}
}
return true;
}
void
MediaCache::AppendMostReusableBlock(BlockList* aBlockList,
@@ -1336,37 +1378,41 @@ MediaCache::Update()
stream, predictedNewDataUse.ToSeconds(), latestNextUse.ToSeconds());
enableReading = predictedNewDataUse < latestNextUse;
}
}
if (enableReading) {
for (uint32_t j = 0; j < i; ++j) {
MediaCacheStream* other = mStreams[j];
- if (other->mResourceID == stream->mResourceID &&
- !other->mClosed && !other->mClient->IsSuspended() &&
- other->mChannelOffset/BLOCK_SIZE == desiredOffset/BLOCK_SIZE) {
+ if (other->mResourceID == stream->mResourceID && !other->mClosed &&
+ !other->mClient->IsSuspended() &&
+ OffsetToBlockIndexUnchecked(other->mChannelOffset) ==
+ OffsetToBlockIndexUnchecked(desiredOffset)) {
// This block is already going to be read by the other stream.
// So don't try to read it from this stream as well.
enableReading = false;
- LOG("Stream %p waiting on same block (%" PRId64 ") from stream %p",
- stream, desiredOffset/BLOCK_SIZE, other);
+ LOG("Stream %p waiting on same block (%" PRId32 ") from stream %p",
+ stream,
+ OffsetToBlockIndexUnchecked(desiredOffset),
+ other);
break;
}
}
}
if (stream->mChannelOffset != desiredOffset && enableReading) {
// We need to seek now.
NS_ASSERTION(stream->mIsTransportSeekable || desiredOffset == 0,
"Trying to seek in a non-seekable stream!");
// Round seek offset down to the start of the block. This is essential
// because we don't want to think we have part of a block already
// in mPartialBlockBuffer.
- stream->mChannelOffset = (desiredOffset/BLOCK_SIZE)*BLOCK_SIZE;
+ stream->mChannelOffset =
+ OffsetToBlockIndexUnchecked(desiredOffset) * BLOCK_SIZE;
actions[i] = stream->mCacheSuspended ? SEEK_AND_RESUME : SEEK;
} else if (enableReading && stream->mCacheSuspended) {
actions[i] = RESUME;
} else if (!enableReading && !stream->mCacheSuspended) {
actions[i] = SUSPEND;
}
}
#ifdef DEBUG
@@ -1559,17 +1605,18 @@ MediaCache::InsertReadaheadBlock(BlockOw
void
MediaCache::AllocateAndWriteBlock(
MediaCacheStream* aStream, MediaCacheStream::ReadMode aMode,
Span<const uint8_t> aData1, Span<const uint8_t> aData2)
{
mReentrantMonitor.AssertCurrentThreadIn();
- int32_t streamBlockIndex = aStream->mChannelOffset/BLOCK_SIZE;
+ int32_t streamBlockIndex =
+ OffsetToBlockIndexUnchecked(aStream->mChannelOffset);
// Remove all cached copies of this block
ResourceStreamIterator iter(this, aStream->mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
while (streamBlockIndex >= int32_t(stream->mBlocks.Length())) {
stream->mBlocks.AppendElement(-1);
}
if (stream->mBlocks[streamBlockIndex] >= 0) {
@@ -1758,40 +1805,52 @@ void
MediaCache::NoteSeek(MediaCacheStream* aStream, int64_t aOldOffset)
{
mReentrantMonitor.AssertCurrentThreadIn();
if (aOldOffset < aStream->mStreamOffset) {
// We seeked forward. Convert blocks from readahead to played.
// Any readahead block that intersects the seeked-over range must
// be converted.
- int32_t blockIndex = aOldOffset/BLOCK_SIZE;
+ int32_t blockIndex = OffsetToBlockIndex(aOldOffset);
+ if (blockIndex < 0) {
+ return;
+ }
int32_t endIndex =
- std::min<int64_t>((aStream->mStreamOffset + BLOCK_SIZE - 1)/BLOCK_SIZE,
- aStream->mBlocks.Length());
+ std::min(OffsetToBlockIndex(aStream->mStreamOffset + (BLOCK_SIZE - 1)),
+ int32_t(aStream->mBlocks.Length()));
+ if (endIndex < 0) {
+ return;
+ }
TimeStamp now = TimeStamp::Now();
while (blockIndex < endIndex) {
int32_t cacheBlockIndex = aStream->mBlocks[blockIndex];
if (cacheBlockIndex >= 0) {
// Marking the block used may not be exactly what we want but
// it's simple
NoteBlockUsage(aStream, cacheBlockIndex, aStream->mStreamOffset,
MediaCacheStream::MODE_PLAYBACK, now);
}
++blockIndex;
}
} else {
// We seeked backward. Convert from played to readahead.
// Any played block that is entirely after the start of the seeked-over
// range must be converted.
int32_t blockIndex =
- (aStream->mStreamOffset + BLOCK_SIZE - 1)/BLOCK_SIZE;
+ OffsetToBlockIndex(aStream->mStreamOffset + (BLOCK_SIZE - 1));
+ if (blockIndex < 0) {
+ return;
+ }
int32_t endIndex =
- std::min<int64_t>((aOldOffset + BLOCK_SIZE - 1)/BLOCK_SIZE,
- aStream->mBlocks.Length());
+ std::min(OffsetToBlockIndex(aOldOffset + (BLOCK_SIZE - 1)),
+ int32_t(aStream->mBlocks.Length()));
+ if (endIndex < 0) {
+ return;
+ }
while (blockIndex < endIndex) {
MOZ_ASSERT(endIndex > 0);
int32_t cacheBlockIndex = aStream->mBlocks[endIndex - 1];
if (cacheBlockIndex >= 0) {
BlockOwner* bo = GetBlockOwner(cacheBlockIndex, aStream);
NS_ASSERTION(bo, "Stream doesn't own its blocks?");
if (bo->mClass == PLAYED_BLOCK) {
aStream->mPlayedBlocks.RemoveBlock(cacheBlockIndex);
@@ -1869,17 +1928,17 @@ MediaCacheStream::NotifyDataReceived(int
int64_t size = aSize;
const char* data = aData;
LOG("Stream %p DataReceived at %" PRId64 " count=%" PRId64,
this, mChannelOffset, aSize);
// We process the data one block (or part of a block) at a time
while (size > 0) {
- uint32_t blockIndex = mChannelOffset/BLOCK_SIZE;
+ uint32_t blockIndex = OffsetToBlockIndexUnchecked(mChannelOffset);
int32_t blockOffset = int32_t(mChannelOffset - blockIndex*BLOCK_SIZE);
int32_t chunkSize = std::min<int64_t>(BLOCK_SIZE - blockOffset, size);
if (blockOffset == 0) {
// We've just started filling this buffer so now is a good time
// to clear this flag.
mMetadataInPartialBlockBuffer = false;
}
@@ -1919,17 +1978,17 @@ MediaCacheStream::NotifyDataReceived(int
}
void
MediaCacheStream::FlushPartialBlockInternal(bool aNotifyAll,
ReentrantMonitorAutoEnter& aReentrantMonitor)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
- int32_t blockOffset = int32_t(mChannelOffset%BLOCK_SIZE);
+ int32_t blockOffset = OffsetInBlock(mChannelOffset);
if (blockOffset > 0) {
LOG("Stream %p writing partial block: [%d] bytes; "
"mStreamOffset [%" PRId64 "] mChannelOffset[%"
PRId64 "] mStreamLength [%" PRId64 "] notifying: [%s]",
this, blockOffset, mStreamOffset, mChannelOffset, mStreamLength,
aNotifyAll ? "yes" : "no");
// Write back the partial block
@@ -2164,23 +2223,25 @@ MediaCacheStream::IsDataCachedToEndOfStr
return false;
return GetCachedDataEndInternal(aOffset) >= mStreamLength;
}
int64_t
MediaCacheStream::GetCachedDataEndInternal(int64_t aOffset)
{
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
- uint32_t startBlockIndex = aOffset/BLOCK_SIZE;
- uint32_t blockIndex = startBlockIndex;
- while (blockIndex < mBlocks.Length() && mBlocks[blockIndex] != -1) {
+ int32_t blockIndex = OffsetToBlockIndex(aOffset);
+ if (blockIndex < 0) {
+ return aOffset;
+ }
+ while (size_t(blockIndex) < mBlocks.Length() && mBlocks[blockIndex] != -1) {
++blockIndex;
}
int64_t result = blockIndex*BLOCK_SIZE;
- if (blockIndex == mChannelOffset/BLOCK_SIZE) {
+ if (blockIndex == OffsetToBlockIndexUnchecked(mChannelOffset)) {
// The block containing mChannelOffset may be partially read but not
// yet committed to the main cache
result = mChannelOffset;
}
if (mStreamLength >= 0) {
// The last block in the cache may only be partially valid, so limit
// the cached range to the stream length
result = std::min(result, mStreamLength);
@@ -2190,47 +2251,50 @@ MediaCacheStream::GetCachedDataEndIntern
int64_t
MediaCacheStream::GetNextCachedDataInternal(int64_t aOffset)
{
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
if (aOffset == mStreamLength)
return -1;
- uint32_t startBlockIndex = aOffset/BLOCK_SIZE;
- uint32_t channelBlockIndex = mChannelOffset/BLOCK_SIZE;
+ int32_t startBlockIndex = OffsetToBlockIndex(aOffset);
+ if (startBlockIndex < 0) {
+ return -1;
+ }
+ int32_t channelBlockIndex = OffsetToBlockIndexUnchecked(mChannelOffset);
if (startBlockIndex == channelBlockIndex &&
aOffset < mChannelOffset) {
// The block containing mChannelOffset is partially read, but not
// yet committed to the main cache. aOffset lies in the partially
// read portion, thus it is effectively cached.
return aOffset;
}
- if (startBlockIndex >= mBlocks.Length())
+ if (size_t(startBlockIndex) >= mBlocks.Length())
return -1;
// Is the current block cached?
if (mBlocks[startBlockIndex] != -1)
return aOffset;
// Count the number of uncached blocks
- bool hasPartialBlock = (mChannelOffset % BLOCK_SIZE) != 0;
- uint32_t blockIndex = startBlockIndex + 1;
+ bool hasPartialBlock = OffsetInBlock(mChannelOffset) != 0;
+ int32_t blockIndex = startBlockIndex + 1;
while (true) {
if ((hasPartialBlock && blockIndex == channelBlockIndex) ||
- (blockIndex < mBlocks.Length() && mBlocks[blockIndex] != -1)) {
+ (size_t(blockIndex) < mBlocks.Length() && mBlocks[blockIndex] != -1)) {
// We at the incoming channel block, which has has data in it,
// or are we at a cached block. Return index of block start.
return blockIndex * BLOCK_SIZE;
}
// No more cached blocks?
- if (blockIndex >= mBlocks.Length())
+ if (size_t(blockIndex) >= mBlocks.Length())
return -1;
++blockIndex;
}
NS_NOTREACHED("Should return in loop");
return -1;
}
@@ -2279,18 +2343,19 @@ MediaCacheStream::Seek(int32_t aWhence,
case PR_SEEK_SET:
newOffset = aOffset;
break;
default:
NS_ERROR("Unknown whence");
return NS_ERROR_FAILURE;
}
- if (newOffset < 0)
+ if (!IsOffsetAllowed(newOffset)) {
return NS_ERROR_FAILURE;
+ }
mStreamOffset = newOffset;
LOG("Stream %p Seek to %" PRId64, this, mStreamOffset);
mMediaCache->NoteSeek(this, oldOffset);
mMediaCache->QueueUpdate();
return NS_OK;
}
@@ -2325,50 +2390,55 @@ MediaCacheStream::Read(char* aBuffer, ui
// Cache the offset in case it is changed again when we are waiting for the
// monitor to be notified to avoid reading at the wrong position.
auto streamOffset = mStreamOffset;
uint32_t count = 0;
// Read one block (or part of a block) at a time
while (count < aCount) {
- uint32_t streamBlock = uint32_t(streamOffset/BLOCK_SIZE);
+ int32_t streamBlock = OffsetToBlockIndex(streamOffset);
+ if (streamBlock < 0) {
+ break;
+ }
uint32_t offsetInStreamBlock = uint32_t(streamOffset - streamBlock*BLOCK_SIZE);
int64_t size = std::min<int64_t>(aCount - count, BLOCK_SIZE - offsetInStreamBlock);
if (mStreamLength >= 0) {
// Don't try to read beyond the end of the stream
int64_t bytesRemaining = mStreamLength - streamOffset;
if (bytesRemaining <= 0) {
// Get out of here and return NS_OK
break;
}
size = std::min(size, bytesRemaining);
// Clamp size until 64-bit file size issues are fixed.
size = std::min(size, int64_t(INT32_MAX));
}
- int32_t cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1;
+ int32_t cacheBlock =
+ size_t(streamBlock) < mBlocks.Length() ? mBlocks[streamBlock] : -1;
if (cacheBlock < 0) {
// We don't have a complete cached block here.
if (count > 0) {
// Some data has been read, so return what we've got instead of
// blocking or trying to find a stream with a partial block.
break;
}
// See if the data is available in the partial cache block of any
// stream reading this resource. We need to do this in case there is
// another stream with this resource that has all the data to the end of
// the stream but the data doesn't end on a block boundary.
MediaCacheStream* streamWithPartialBlock = nullptr;
MediaCache::ResourceStreamIterator iter(mMediaCache, mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
- if (uint32_t(stream->mChannelOffset/BLOCK_SIZE) == streamBlock &&
+ if (OffsetToBlockIndexUnchecked(stream->mChannelOffset) ==
+ streamBlock &&
streamOffset < stream->mChannelOffset) {
streamWithPartialBlock = stream;
break;
}
}
if (streamWithPartialBlock) {
// We can just use the data in mPartialBlockBuffer. In fact we should
// use it rather than waiting for the block to fill and land in
@@ -2447,35 +2517,39 @@ MediaCacheStream::ReadFromCache(char* aB
uint32_t count = 0;
int64_t streamOffset = aOffset;
while (count < aCount) {
if (mClosed) {
// We need to check |mClosed| in each iteration which might be changed
// after calling |mMediaCache->ReadCacheFile|.
return NS_ERROR_FAILURE;
}
- uint32_t streamBlock = uint32_t(streamOffset/BLOCK_SIZE);
+ int32_t streamBlock = OffsetToBlockIndex(streamOffset);
+ if (streamBlock < 0) {
+ break;
+ }
uint32_t offsetInStreamBlock =
uint32_t(streamOffset - streamBlock*BLOCK_SIZE);
int64_t size = std::min<int64_t>(aCount - count, BLOCK_SIZE - offsetInStreamBlock);
if (mStreamLength >= 0) {
// Don't try to read beyond the end of the stream
int64_t bytesRemaining = mStreamLength - streamOffset;
if (bytesRemaining <= 0) {
return NS_ERROR_FAILURE;
}
size = std::min(size, bytesRemaining);
// Clamp size until 64-bit file size issues are fixed.
size = std::min(size, int64_t(INT32_MAX));
}
int32_t bytes;
- uint32_t channelBlock = uint32_t(mChannelOffset/BLOCK_SIZE);
- int32_t cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1;
+ int32_t channelBlock = OffsetToBlockIndexUnchecked(mChannelOffset);
+ int32_t cacheBlock =
+ size_t(streamBlock) < mBlocks.Length() ? mBlocks[streamBlock] : -1;
if (channelBlock == streamBlock && streamOffset < mChannelOffset) {
// We can just use the data in mPartialBlockBuffer. In fact we should
// use it rather than waiting for the block to fill and land in
// the cache.
// Clamp bytes until 64-bit file size issues are fixed.
int64_t toCopy = std::min<int64_t>(size, mChannelOffset - streamOffset);
bytes = std::min(toCopy, int64_t(INT32_MAX));
MOZ_ASSERT(bytes >= 0 && bytes <= toCopy, "Bytes out of range.");