Bug 1415397 - use Span<> to replace low level pointer arithmetic in ReadFromCache(). r=bechen,gerald

MozReview-Commit-ID: HH6KXtMfSIJ

--HG--
extra : rebase_source : a6fe803e3e89a0f89c225e2415a233a945a6a716
extra : intermediate-source : 8c37409d77f675c89fd9b2fb276aa80c57d4eb7f
extra : source : 1f0e76b45b7d89bcc10d59e302fe4b08e87cc96c
This commit is contained in:
JW Wang 2017-11-02 11:36:56 +08:00
Родитель ecc1a99b7d
Коммит f53f02fb3e
2 изменённых файлов: 107 добавлений и 50 удалений

Просмотреть файл

@ -2471,6 +2471,72 @@ MediaCacheStream::ThrottleReadahead(bool bThrottle)
}
}
uint32_t
MediaCacheStream::ReadPartialBlock(int64_t aOffset, Span<char> aBuffer)
{
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
MOZ_ASSERT(IsOffsetAllowed(aOffset));
if (OffsetToBlockIndexUnchecked(mChannelOffset) !=
OffsetToBlockIndexUnchecked(aOffset) ||
aOffset >= mChannelOffset) {
// Not in the partial block or no data to read.
return 0;
}
auto source = MakeSpan<const uint8_t>(
mPartialBlockBuffer.get() + OffsetInBlock(aOffset),
OffsetInBlock(mChannelOffset) - OffsetInBlock(aOffset));
// We have |source.Length() <= BLOCK_SIZE < INT32_MAX| to guarantee
// that |bytesToRead| can fit into a uint32_t.
uint32_t bytesToRead = std::min(aBuffer.Length(), source.Length());
memcpy(aBuffer.Elements(), source.Elements(), bytesToRead);
return bytesToRead;
}
Result<uint32_t, nsresult>
MediaCacheStream::ReadBlockFromCache(int64_t aOffset, Span<char> aBuffer)
{
mMediaCache->GetReentrantMonitor().AssertCurrentThreadIn();
MOZ_ASSERT(IsOffsetAllowed(aOffset));
// OffsetToBlockIndexUnchecked() is always non-negative.
uint32_t index = OffsetToBlockIndexUnchecked(aOffset);
int32_t cacheBlock = index < mBlocks.Length() ? mBlocks[index] : -1;
if (cacheBlock < 0) {
// Not in the cache.
return 0;
}
if (aBuffer.Length() > size_t(BLOCK_SIZE)) {
// Clamp the buffer to avoid overflow below since we will read at most
// BLOCK_SIZE bytes.
aBuffer = aBuffer.First(BLOCK_SIZE);
}
// |BLOCK_SIZE - OffsetInBlock(aOffset)| <= BLOCK_SIZE
int32_t bytesToRead =
std::min<int32_t>(BLOCK_SIZE - OffsetInBlock(aOffset), aBuffer.Length());
int32_t bytesRead = 0;
nsresult rv =
mMediaCache->ReadCacheFile(cacheBlock * BLOCK_SIZE + OffsetInBlock(aOffset),
aBuffer.Elements(),
bytesToRead,
&bytesRead);
// Ensure |cacheBlock * BLOCK_SIZE + OffsetInBlock(aOffset)| won't overflow.
static_assert(INT64_MAX >= BLOCK_SIZE * (uint32_t(INT32_MAX) + 1),
"BLOCK_SIZE too large!");
if (NS_FAILED(rv)) {
nsCString name;
GetErrorName(rv, name);
LOGE("Stream %p ReadCacheFile failed, rv=%s", this, name.Data());
return mozilla::Err(rv);
}
return bytesRead;
}
int64_t
MediaCacheStream::Tell()
{
@ -2615,67 +2681,49 @@ MediaCacheStream::ReadAt(int64_t aOffset, char* aBuffer,
}
nsresult
MediaCacheStream::ReadFromCache(char* aBuffer, int64_t aOffset, int64_t aCount)
MediaCacheStream::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
{
ReentrantMonitorAutoEnter mon(mMediaCache->GetReentrantMonitor());
// The buffer we are about to fill.
auto buffer = MakeSpan<char>(aBuffer, aCount);
// Read one block (or part of a block) at a time
uint32_t count = 0;
int64_t streamOffset = aOffset;
while (count < aCount) {
while (!buffer.IsEmpty()) {
if (mClosed) {
// We need to check |mClosed| in each iteration which might be changed
// after calling |mMediaCache->ReadCacheFile|.
return NS_ERROR_FAILURE;
}
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));
if (!IsOffsetAllowed(streamOffset)) {
LOGE("Stream %p invalid offset=%" PRId64, this, streamOffset);
return NS_ERROR_ILLEGAL_VALUE;
}
int32_t bytes;
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.");
memcpy(aBuffer + count,
mPartialBlockBuffer.get() + offsetInStreamBlock, bytes);
} else {
if (cacheBlock < 0) {
// We expect all blocks to be cached! Fail!
return NS_ERROR_FAILURE;
}
int64_t offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
MOZ_ASSERT(size >= 0 && size <= INT32_MAX, "Size out of range.");
nsresult rv = mMediaCache->ReadCacheFile(
offset, aBuffer + count, int32_t(size), &bytes);
if (NS_FAILED(rv)) {
return rv;
}
Result<uint32_t, nsresult> rv = ReadBlockFromCache(streamOffset, buffer);
if (rv.isErr()) {
return rv.unwrapErr();
}
streamOffset += bytes;
count += bytes;
uint32_t bytes = rv.unwrap();
if (bytes > 0) {
// Read data from the cache successfully. Let's try next block.
streamOffset += bytes;
buffer = buffer.From(bytes);
continue;
}
// The partial block is our last chance to get data.
bytes = ReadPartialBlock(streamOffset, buffer);
if (bytes < buffer.Length()) {
// Not enough data to read.
return NS_ERROR_FAILURE;
}
// Return for we've got all the requested bytes.
return NS_OK;
}
return NS_OK;

Просмотреть файл

@ -8,6 +8,7 @@
#define MediaCache_h_
#include "Intervals.h"
#include "mozilla/Result.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsHashKeys.h"
@ -314,9 +315,7 @@ public:
// in the cache. Will not mark blocks as read. Can be called from the main
// thread. It's the caller's responsibility to wrap the call in a pin/unpin,
// and also to check that the range they want is cached before calling this.
nsresult ReadFromCache(char* aBuffer,
int64_t aOffset,
int64_t aCount);
nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount);
// IsDataCachedToEndOfStream returns true if all the data from
// aOffset to the end of the stream (the server-reported end, if the
@ -426,6 +425,16 @@ private:
int32_t mCount;
};
// Read data from the partial block and return the number of bytes read
// successfully. 0 if aOffset is not an offset in the partial block or there
// is nothing to read.
uint32_t ReadPartialBlock(int64_t aOffset, Span<char> aBuffer);
// Read data from the cache block specified by aOffset. Return the number of
// bytes read successfully or an error code if any failure.
Result<uint32_t, nsresult> ReadBlockFromCache(int64_t aOffset,
Span<char> aBuffer);
// Returns the end of the bytes starting at the given offset
// which are in cache.
// This method assumes that the cache monitor is held and can be called on