зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
ecc1a99b7d
Коммит
f53f02fb3e
|
@ -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();
|
||||
}
|
||||
|
||||
uint32_t bytes = rv.unwrap();
|
||||
if (bytes > 0) {
|
||||
// Read data from the cache successfully. Let's try next block.
|
||||
streamOffset += bytes;
|
||||
count += 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
|
||||
|
|
Загрузка…
Ссылка в новой задаче