mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 913807 - HTTP cache v2: file I/O, off by default, r=honzab
This commit is contained in:
parent
894ba59236
commit
f3cf185b1e
1634
netwerk/cache2/CacheFile.cpp
Normal file
1634
netwerk/cache2/CacheFile.cpp
Normal file
File diff suppressed because it is too large
Load Diff
217
netwerk/cache2/CacheFile.h
Normal file
217
netwerk/cache2/CacheFile.h
Normal file
@ -0,0 +1,217 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CacheFile__h__
|
||||
#define CacheFile__h__
|
||||
|
||||
#include "CacheFileChunk.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "CacheFileIOManager.h"
|
||||
#include "CacheFileMetadata.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
class nsIInputStream;
|
||||
class nsIOutputStream;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheFileInputStream;
|
||||
class CacheFileOutputStream;
|
||||
class MetadataWriteTimer;
|
||||
|
||||
#define CACHEFILELISTENER_IID \
|
||||
{ /* 95e7f284-84ba-48f9-b1fc-3a7336b4c33c */ \
|
||||
0x95e7f284, \
|
||||
0x84ba, \
|
||||
0x48f9, \
|
||||
{0xb1, 0xfc, 0x3a, 0x73, 0x36, 0xb4, 0xc3, 0x3c} \
|
||||
}
|
||||
|
||||
class CacheFileListener : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILELISTENER_IID)
|
||||
|
||||
NS_IMETHOD OnFileReady(nsresult aResult, bool aIsNew) = 0;
|
||||
NS_IMETHOD OnFileDoomed(nsresult aResult) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileListener, CACHEFILELISTENER_IID)
|
||||
|
||||
|
||||
class CacheFile : public CacheFileChunkListener
|
||||
, public CacheFileIOListener
|
||||
, public CacheFileMetadataListener
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
CacheFile();
|
||||
|
||||
nsresult Init(const nsACString &aKey,
|
||||
bool aCreateNew,
|
||||
bool aMemoryOnly,
|
||||
bool aPriority,
|
||||
bool aKeyIsHash,
|
||||
CacheFileListener *aCallback);
|
||||
|
||||
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
|
||||
CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk);
|
||||
|
||||
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
|
||||
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
|
||||
nsresult aResult);
|
||||
NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
|
||||
NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
|
||||
NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult);
|
||||
|
||||
NS_IMETHOD OnMetadataRead(nsresult aResult);
|
||||
NS_IMETHOD OnMetadataWritten(nsresult aResult);
|
||||
|
||||
NS_IMETHOD OpenInputStream(nsIInputStream **_retval);
|
||||
NS_IMETHOD OpenOutputStream(nsIOutputStream **_retval);
|
||||
NS_IMETHOD SetMemoryOnly();
|
||||
NS_IMETHOD Doom(CacheFileListener *aCallback);
|
||||
|
||||
nsresult ThrowMemoryCachedData();
|
||||
|
||||
// metadata forwarders
|
||||
nsresult GetElement(const char *aKey, const char **_retval);
|
||||
nsresult SetElement(const char *aKey, const char *aValue);
|
||||
nsresult ElementsSize(uint32_t *_retval);
|
||||
nsresult SetExpirationTime(uint32_t aExpirationTime);
|
||||
nsresult GetExpirationTime(uint32_t *_retval);
|
||||
nsresult SetLastModified(uint32_t aLastModified);
|
||||
nsresult GetLastModified(uint32_t *_retval);
|
||||
nsresult GetLastFetched(uint32_t *_retval);
|
||||
nsresult GetFetchCount(uint32_t *_retval);
|
||||
|
||||
bool DataSize(int64_t* aSize);
|
||||
void Key(nsACString& aKey) { aKey = mKey; }
|
||||
|
||||
private:
|
||||
friend class CacheFileChunk;
|
||||
friend class CacheFileInputStream;
|
||||
friend class CacheFileOutputStream;
|
||||
friend class CacheFileAutoLock;
|
||||
friend class MetadataWriteTimer;
|
||||
friend class MetadataListenerHelper;
|
||||
|
||||
virtual ~CacheFile();
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
void AssertOwnsLock();
|
||||
void ReleaseOutsideLock(nsISupports *aObject);
|
||||
|
||||
nsresult GetChunk(uint32_t aIndex, bool aWriter,
|
||||
CacheFileChunkListener *aCallback,
|
||||
CacheFileChunk **_retval);
|
||||
nsresult GetChunkLocked(uint32_t aIndex, bool aWriter,
|
||||
CacheFileChunkListener *aCallback,
|
||||
CacheFileChunk **_retval);
|
||||
nsresult RemoveChunk(CacheFileChunk *aChunk);
|
||||
|
||||
nsresult RemoveInput(CacheFileInputStream *aInput);
|
||||
nsresult RemoveOutput(CacheFileOutputStream *aOutput);
|
||||
nsresult NotifyChunkListener(CacheFileChunkListener *aCallback,
|
||||
nsIEventTarget *aTarget,
|
||||
nsresult aResult,
|
||||
uint32_t aChunkIdx,
|
||||
CacheFileChunk *aChunk);
|
||||
nsresult QueueChunkListener(uint32_t aIndex,
|
||||
CacheFileChunkListener *aCallback);
|
||||
nsresult NotifyChunkListeners(uint32_t aIndex, nsresult aResult,
|
||||
CacheFileChunk *aChunk);
|
||||
bool HaveChunkListeners(uint32_t aIndex);
|
||||
void NotifyListenersAboutOutputRemoval();
|
||||
|
||||
bool IsDirty();
|
||||
void WriteMetadataIfNeeded();
|
||||
void PostWriteTimer();
|
||||
|
||||
static PLDHashOperator WriteAllCachedChunks(const uint32_t& aIdx,
|
||||
nsRefPtr<CacheFileChunk>& aChunk,
|
||||
void* aClosure);
|
||||
|
||||
static PLDHashOperator FailListenersIfNonExistentChunk(
|
||||
const uint32_t& aIdx,
|
||||
nsAutoPtr<mozilla::net::ChunkListeners>& aListeners,
|
||||
void* aClosure);
|
||||
|
||||
static PLDHashOperator FailUpdateListeners(const uint32_t& aIdx,
|
||||
nsRefPtr<CacheFileChunk>& aChunk,
|
||||
void* aClosure);
|
||||
|
||||
nsresult PadChunkWithZeroes(uint32_t aChunkIdx);
|
||||
|
||||
mozilla::Mutex mLock;
|
||||
bool mOpeningFile;
|
||||
bool mReady;
|
||||
bool mMemoryOnly;
|
||||
bool mDataAccessed;
|
||||
bool mDataIsDirty;
|
||||
bool mWritingMetadata;
|
||||
bool mKeyIsHash;
|
||||
nsresult mStatus;
|
||||
int64_t mDataSize;
|
||||
nsCString mKey;
|
||||
|
||||
nsRefPtr<CacheFileHandle> mHandle;
|
||||
nsRefPtr<CacheFileMetadata> mMetadata;
|
||||
nsCOMPtr<CacheFileListener> mListener;
|
||||
nsRefPtr<MetadataWriteTimer> mTimer;
|
||||
|
||||
nsRefPtrHashtable<nsUint32HashKey, CacheFileChunk> mChunks;
|
||||
nsClassHashtable<nsUint32HashKey, ChunkListeners> mChunkListeners;
|
||||
nsRefPtrHashtable<nsUint32HashKey, CacheFileChunk> mCachedChunks;
|
||||
|
||||
nsTArray<CacheFileInputStream*> mInputs;
|
||||
CacheFileOutputStream *mOutput;
|
||||
|
||||
nsTArray<nsISupports*> mObjsToRelease;
|
||||
};
|
||||
|
||||
class CacheFileAutoLock {
|
||||
public:
|
||||
CacheFileAutoLock(CacheFile *aFile)
|
||||
: mFile(aFile)
|
||||
, mLocked(true)
|
||||
{
|
||||
mFile->Lock();
|
||||
}
|
||||
~CacheFileAutoLock()
|
||||
{
|
||||
if (mLocked)
|
||||
mFile->Unlock();
|
||||
}
|
||||
void Lock()
|
||||
{
|
||||
MOZ_ASSERT(!mLocked);
|
||||
mFile->Lock();
|
||||
mLocked = true;
|
||||
}
|
||||
void Unlock()
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
mFile->Unlock();
|
||||
mLocked = false;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<CacheFile> mFile;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
675
netwerk/cache2/CacheFileChunk.cpp
Normal file
675
netwerk/cache2/CacheFileChunk.cpp
Normal file
@ -0,0 +1,675 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CacheFileChunk.h"
|
||||
|
||||
#include "CacheLog.h"
|
||||
#include "CacheFile.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsAlgorithm.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
#define kMinBufSize 512
|
||||
|
||||
class NotifyUpdateListenerEvent : public nsRunnable {
|
||||
public:
|
||||
NotifyUpdateListenerEvent(CacheFileChunkListener *aCallback,
|
||||
CacheFileChunk *aChunk)
|
||||
: mCallback(aCallback)
|
||||
, mChunk(aChunk)
|
||||
{
|
||||
LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
|
||||
this));
|
||||
MOZ_COUNT_CTOR(NotifyUpdateListenerEvent);
|
||||
}
|
||||
|
||||
~NotifyUpdateListenerEvent()
|
||||
{
|
||||
LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
|
||||
this));
|
||||
MOZ_COUNT_DTOR(NotifyUpdateListenerEvent);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
|
||||
|
||||
mCallback->OnChunkUpdated(mChunk);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<CacheFileChunkListener> mCallback;
|
||||
nsRefPtr<CacheFileChunk> mChunk;
|
||||
};
|
||||
|
||||
|
||||
class ValidityPair {
|
||||
public:
|
||||
ValidityPair(uint32_t aOffset, uint32_t aLen)
|
||||
: mOffset(aOffset), mLen(aLen)
|
||||
{}
|
||||
|
||||
ValidityPair& operator=(const ValidityPair& aOther) {
|
||||
mOffset = aOther.mOffset;
|
||||
mLen = aOther.mLen;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Overlaps(const ValidityPair& aOther) const {
|
||||
if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) ||
|
||||
(aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LessThan(const ValidityPair& aOther) const {
|
||||
if (mOffset < aOther.mOffset)
|
||||
return true;
|
||||
|
||||
if (mOffset == aOther.mOffset && mLen < aOther.mLen)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Merge(const ValidityPair& aOther) {
|
||||
MOZ_ASSERT(Overlaps(aOther));
|
||||
|
||||
uint32_t offset = std::min(mOffset, aOther.mOffset);
|
||||
uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
|
||||
|
||||
mOffset = offset;
|
||||
mLen = end - offset;
|
||||
}
|
||||
|
||||
uint32_t Offset() { return mOffset; }
|
||||
uint32_t Len() { return mLen; }
|
||||
|
||||
private:
|
||||
uint32_t mOffset;
|
||||
uint32_t mLen;
|
||||
};
|
||||
|
||||
|
||||
NS_IMPL_ADDREF(CacheFileChunk)
|
||||
NS_IMETHODIMP_(nsrefcnt)
|
||||
CacheFileChunk::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
nsrefcnt count = --mRefCnt;
|
||||
NS_LOG_RELEASE(this, count, "CacheFileChunk");
|
||||
|
||||
if (0 == count) {
|
||||
mRefCnt = 1;
|
||||
delete (this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mRemovingChunk && count == 1) {
|
||||
mFile->RemoveChunk(this);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
|
||||
NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END_THREADSAFE
|
||||
|
||||
CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex)
|
||||
: mIndex(aIndex)
|
||||
, mState(INITIAL)
|
||||
, mIsDirty(false)
|
||||
, mRemovingChunk(false)
|
||||
, mDataSize(0)
|
||||
, mBuf(nullptr)
|
||||
, mBufSize(0)
|
||||
, mRWBuf(nullptr)
|
||||
, mRWBufSize(0)
|
||||
, mReadHash(0)
|
||||
, mFile(aFile)
|
||||
{
|
||||
LOG(("CacheFileChunk::CacheFileChunk() [this=%p]", this));
|
||||
MOZ_COUNT_CTOR(CacheFileChunk);
|
||||
}
|
||||
|
||||
CacheFileChunk::~CacheFileChunk()
|
||||
{
|
||||
LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
|
||||
MOZ_COUNT_DTOR(CacheFileChunk);
|
||||
|
||||
if (mBuf) {
|
||||
free(mBuf);
|
||||
mBuf = nullptr;
|
||||
mBufSize = 0;
|
||||
}
|
||||
|
||||
if (mRWBuf) {
|
||||
free(mRWBuf);
|
||||
mRWBuf = nullptr;
|
||||
mRWBufSize = 0;
|
||||
}
|
||||
|
||||
DoMemoryReport(MemorySize());
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileChunk::InitNew(CacheFileChunkListener *aCallback)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback));
|
||||
|
||||
MOZ_ASSERT(mState == INITIAL);
|
||||
MOZ_ASSERT(!mBuf);
|
||||
MOZ_ASSERT(!mRWBuf);
|
||||
|
||||
mBuf = static_cast<char *>(moz_xmalloc(kMinBufSize));
|
||||
mBufSize = kMinBufSize;
|
||||
mDataSize = 0;
|
||||
mState = READY;
|
||||
mIsDirty = true;
|
||||
|
||||
DoMemoryReport(MemorySize());
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
|
||||
CacheHashUtils::Hash16_t aHash,
|
||||
CacheFileChunkListener *aCallback)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
|
||||
this, aHandle, aLen, aCallback));
|
||||
|
||||
MOZ_ASSERT(mState == INITIAL);
|
||||
MOZ_ASSERT(!mBuf);
|
||||
MOZ_ASSERT(!mRWBuf);
|
||||
MOZ_ASSERT(aLen);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
mRWBuf = static_cast<char *>(moz_xmalloc(aLen));
|
||||
mRWBufSize = aLen;
|
||||
|
||||
DoMemoryReport(MemorySize());
|
||||
|
||||
rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize, mRWBuf, aLen,
|
||||
this);
|
||||
if (NS_FAILED(rv)) {
|
||||
mState = READING; // TODO: properly handle error states
|
||||
// mState = ERROR;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mState = READING;
|
||||
mListener = aCallback;
|
||||
mDataSize = aLen;
|
||||
mReadHash = aHash;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::Write(CacheFileHandle *aHandle,
|
||||
CacheFileChunkListener *aCallback)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
|
||||
this, aHandle, aCallback));
|
||||
|
||||
MOZ_ASSERT(mState == READY);
|
||||
MOZ_ASSERT(!mRWBuf);
|
||||
MOZ_ASSERT(mBuf);
|
||||
MOZ_ASSERT(mDataSize); // Don't write chunk when it is empty
|
||||
|
||||
nsresult rv;
|
||||
|
||||
mRWBuf = mBuf;
|
||||
mRWBufSize = mBufSize;
|
||||
mBuf = nullptr;
|
||||
mBufSize = 0;
|
||||
|
||||
rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize, mRWBuf,
|
||||
mDataSize, false, this);
|
||||
if (NS_FAILED(rv)) {
|
||||
mState = WRITING; // TODO: properly handle error states
|
||||
// mState = ERROR;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mState = WRITING;
|
||||
mListener = aCallback;
|
||||
mIsDirty = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
|
||||
this, aCallback));
|
||||
|
||||
MOZ_ASSERT(mFile->mOutput);
|
||||
MOZ_ASSERT(IsReady());
|
||||
|
||||
#ifdef DEBUG
|
||||
for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
|
||||
MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
|
||||
}
|
||||
#endif
|
||||
|
||||
ChunkListenerItem *item = new ChunkListenerItem();
|
||||
item->mTarget = NS_GetCurrentThread();
|
||||
item->mCallback = aCallback;
|
||||
|
||||
mUpdateListeners.AppendElement(item);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
|
||||
|
||||
MOZ_ASSERT(IsReady());
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
|
||||
ChunkListenerItem *item = mUpdateListeners[i];
|
||||
|
||||
if (item->mCallback == aCallback) {
|
||||
mUpdateListeners.RemoveElementAt(i);
|
||||
delete item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for ( ; i < mUpdateListeners.Length() ; i++) {
|
||||
MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::NotifyUpdateListeners()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
|
||||
|
||||
MOZ_ASSERT(IsReady());
|
||||
|
||||
nsresult rv, rv2;
|
||||
|
||||
rv = NS_OK;
|
||||
for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
|
||||
ChunkListenerItem *item = mUpdateListeners[i];
|
||||
|
||||
LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
|
||||
"[this=%p]", item->mCallback.get(), this));
|
||||
|
||||
nsRefPtr<NotifyUpdateListenerEvent> ev;
|
||||
ev = new NotifyUpdateListenerEvent(item->mCallback, this);
|
||||
rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
|
||||
rv = rv2;
|
||||
delete item;
|
||||
}
|
||||
|
||||
mUpdateListeners.Clear();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CacheFileChunk::Index()
|
||||
{
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
CacheHashUtils::Hash16_t
|
||||
CacheFileChunk::Hash()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
MOZ_ASSERT(mBuf);
|
||||
MOZ_ASSERT(!mListener);
|
||||
MOZ_ASSERT(IsReady());
|
||||
|
||||
return CacheHashUtils::Hash16(BufForReading(), mDataSize);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CacheFileChunk::DataSize()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
return mDataSize;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?");
|
||||
MOZ_ASSERT(aOffset <= mDataSize);
|
||||
|
||||
LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]",
|
||||
this, aOffset, aLen, aEOF));
|
||||
|
||||
mIsDirty = true;
|
||||
|
||||
int64_t fileSize = kChunkSize * mIndex + aOffset + aLen;
|
||||
bool notify = false;
|
||||
|
||||
if (fileSize > mFile->mDataSize)
|
||||
mFile->mDataSize = fileSize;
|
||||
|
||||
if (aOffset + aLen > mDataSize) {
|
||||
mDataSize = aOffset + aLen;
|
||||
notify = true;
|
||||
}
|
||||
|
||||
if (mState == READY || mState == WRITING) {
|
||||
MOZ_ASSERT(mValidityMap.Length() == 0);
|
||||
|
||||
if (notify)
|
||||
NotifyUpdateListeners();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We're still waiting for data from the disk. This chunk cannot be used by
|
||||
// input stream, so there must be no update listener. We also need to keep
|
||||
// track of where the data is written so that we can correctly merge the new
|
||||
// data with the old one.
|
||||
|
||||
MOZ_ASSERT(mUpdateListeners.Length() == 0);
|
||||
MOZ_ASSERT(mState == READING);
|
||||
|
||||
ValidityPair pair(aOffset, aLen);
|
||||
|
||||
if (mValidityMap.Length() == 0) {
|
||||
mValidityMap.AppendElement(pair);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Find out where to place this pair into the map, it can overlap with
|
||||
// one preceding pair and all subsequent pairs.
|
||||
uint32_t pos = 0;
|
||||
for (pos = mValidityMap.Length() ; pos > 0 ; pos--) {
|
||||
if (mValidityMap[pos-1].LessThan(pair)) {
|
||||
if (mValidityMap[pos-1].Overlaps(pair)) {
|
||||
// Merge with the preceding pair
|
||||
mValidityMap[pos-1].Merge(pair);
|
||||
pos--; // Point to the updated pair
|
||||
}
|
||||
else {
|
||||
if (pos == mValidityMap.Length())
|
||||
mValidityMap.AppendElement(pair);
|
||||
else
|
||||
mValidityMap.InsertElementAt(pos, pair);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pos)
|
||||
mValidityMap.InsertElementAt(0, pair);
|
||||
|
||||
// Now pos points to merged or inserted pair, check whether it overlaps with
|
||||
// subsequent pairs.
|
||||
while (pos + 1 < mValidityMap.Length()) {
|
||||
if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) {
|
||||
mValidityMap[pos].Merge(mValidityMap[pos + 1]);
|
||||
mValidityMap.RemoveElementAt(pos + 1);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
|
||||
{
|
||||
MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
|
||||
nsresult aResult)
|
||||
{
|
||||
LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]",
|
||||
this, aHandle, aResult));
|
||||
|
||||
nsCOMPtr<CacheFileChunkListener> listener;
|
||||
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
|
||||
MOZ_ASSERT(mState == WRITING);
|
||||
MOZ_ASSERT(mListener);
|
||||
|
||||
#if 0
|
||||
// TODO: properly handle error states
|
||||
if (NS_FAILED(aResult)) {
|
||||
mState = ERROR;
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
mState = READY;
|
||||
if (!mBuf) {
|
||||
mBuf = mRWBuf;
|
||||
mBufSize = mRWBufSize;
|
||||
}
|
||||
else {
|
||||
free(mRWBuf);
|
||||
}
|
||||
|
||||
mRWBuf = nullptr;
|
||||
mRWBufSize = 0;
|
||||
|
||||
DoMemoryReport(MemorySize());
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
|
||||
mListener.swap(listener);
|
||||
}
|
||||
|
||||
listener->OnChunkWritten(aResult, this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
|
||||
nsresult aResult)
|
||||
{
|
||||
LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
|
||||
this, aHandle, aResult));
|
||||
|
||||
nsCOMPtr<CacheFileChunkListener> listener;
|
||||
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
|
||||
MOZ_ASSERT(mState == READING);
|
||||
MOZ_ASSERT(mListener);
|
||||
|
||||
if (NS_SUCCEEDED(aResult)) {
|
||||
CacheHashUtils::Hash16_t hash = CacheHashUtils::Hash16(mRWBuf,
|
||||
mRWBufSize);
|
||||
if (hash != mReadHash) {
|
||||
LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
|
||||
" %hx, hash in metadata is %hx. [this=%p, idx=%d]",
|
||||
hash, mReadHash, this, mIndex));
|
||||
aResult = NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
else {
|
||||
if (!mBuf) {
|
||||
// Just swap the buffers if we don't have mBuf yet
|
||||
MOZ_ASSERT(mDataSize == mRWBufSize);
|
||||
mBuf = mRWBuf;
|
||||
mBufSize = mRWBufSize;
|
||||
mRWBuf = nullptr;
|
||||
mRWBufSize = 0;
|
||||
} else {
|
||||
// Merge data with write buffer
|
||||
if (mRWBufSize < mBufSize) {
|
||||
mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize));
|
||||
mRWBufSize = mBufSize;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) {
|
||||
memcpy(mRWBuf + mValidityMap[i].Offset(),
|
||||
mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len());
|
||||
}
|
||||
|
||||
free(mBuf);
|
||||
mBuf = mRWBuf;
|
||||
mBufSize = mRWBufSize;
|
||||
mRWBuf = nullptr;
|
||||
mRWBufSize = 0;
|
||||
|
||||
DoMemoryReport(MemorySize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
#if 0
|
||||
// TODO: properly handle error states
|
||||
mState = ERROR;
|
||||
#endif
|
||||
mState = READY;
|
||||
mDataSize = 0;
|
||||
}
|
||||
else {
|
||||
mState = READY;
|
||||
}
|
||||
|
||||
mListener.swap(listener);
|
||||
}
|
||||
|
||||
listener->OnChunkRead(aResult, this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
|
||||
{
|
||||
MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
|
||||
{
|
||||
MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
bool
|
||||
CacheFileChunk::IsReady()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
return (mState == READY || mState == WRITING);
|
||||
}
|
||||
|
||||
bool
|
||||
CacheFileChunk::IsDirty()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
return mIsDirty;
|
||||
}
|
||||
|
||||
char *
|
||||
CacheFileChunk::BufForWriting()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize()
|
||||
|
||||
MOZ_ASSERT((mState == READY && !mRWBuf) ||
|
||||
(mState == WRITING && mRWBuf) ||
|
||||
(mState == READING && mRWBuf));
|
||||
|
||||
return mBuf;
|
||||
}
|
||||
|
||||
const char *
|
||||
CacheFileChunk::BufForReading()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) ||
|
||||
(mState == WRITING && mRWBuf));
|
||||
|
||||
return mBuf ? mBuf : mRWBuf;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileChunk::EnsureBufSize(uint32_t aBufSize)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
if (mBufSize >= aBufSize)
|
||||
return;
|
||||
|
||||
bool copy = false;
|
||||
if (!mBuf && mState == WRITING) {
|
||||
// We need to duplicate the data that is being written on the background
|
||||
// thread, so make sure that all the data fits into the new buffer.
|
||||
copy = true;
|
||||
|
||||
if (mRWBufSize > aBufSize)
|
||||
aBufSize = mRWBufSize;
|
||||
}
|
||||
|
||||
// find smallest power of 2 greater than or equal to aBufSize
|
||||
aBufSize--;
|
||||
aBufSize |= aBufSize >> 1;
|
||||
aBufSize |= aBufSize >> 2;
|
||||
aBufSize |= aBufSize >> 4;
|
||||
aBufSize |= aBufSize >> 8;
|
||||
aBufSize |= aBufSize >> 16;
|
||||
aBufSize++;
|
||||
|
||||
const uint32_t minBufSize = kMinBufSize;
|
||||
const uint32_t maxBufSize = kChunkSize;
|
||||
aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
|
||||
|
||||
mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize));
|
||||
mBufSize = aBufSize;
|
||||
|
||||
if (copy)
|
||||
memcpy(mBuf, mRWBuf, mRWBufSize);
|
||||
|
||||
DoMemoryReport(MemorySize());
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
143
netwerk/cache2/CacheFileChunk.h
Normal file
143
netwerk/cache2/CacheFileChunk.h
Normal file
@ -0,0 +1,143 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CacheFileChunk__h__
|
||||
#define CacheFileChunk__h__
|
||||
|
||||
#include "CacheFileIOManager.h"
|
||||
#include "CacheStorageService.h"
|
||||
#include "CacheHashUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
#define kChunkSize 16384
|
||||
#define kEmptyChunkHash 0xA8CA
|
||||
|
||||
class CacheFileChunk;
|
||||
class CacheFile;
|
||||
class ValidityPair;
|
||||
|
||||
|
||||
#define CACHEFILECHUNKLISTENER_IID \
|
||||
{ /* baf16149-2ab5-499c-a9c2-5904eb95c288 */ \
|
||||
0xbaf16149, \
|
||||
0x2ab5, \
|
||||
0x499c, \
|
||||
{0xa9, 0xc2, 0x59, 0x04, 0xeb, 0x95, 0xc2, 0x88} \
|
||||
}
|
||||
|
||||
class CacheFileChunkListener : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILECHUNKLISTENER_IID)
|
||||
|
||||
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) = 0;
|
||||
NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) = 0;
|
||||
NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
|
||||
CacheFileChunk *aChunk) = 0;
|
||||
NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileChunkListener,
|
||||
CACHEFILECHUNKLISTENER_IID)
|
||||
|
||||
|
||||
class ChunkListenerItem {
|
||||
public:
|
||||
ChunkListenerItem() { MOZ_COUNT_CTOR(ChunkListenerItem); }
|
||||
~ChunkListenerItem() { MOZ_COUNT_DTOR(ChunkListenerItem); }
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
nsCOMPtr<CacheFileChunkListener> mCallback;
|
||||
};
|
||||
|
||||
class ChunkListeners {
|
||||
public:
|
||||
ChunkListeners() { MOZ_COUNT_CTOR(ChunkListeners); }
|
||||
~ChunkListeners() { MOZ_COUNT_DTOR(ChunkListeners); }
|
||||
|
||||
nsTArray<ChunkListenerItem *> mItems;
|
||||
};
|
||||
|
||||
class CacheFileChunk : public CacheFileIOListener
|
||||
, public CacheMemoryConsumer
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
CacheFileChunk(CacheFile *aFile, uint32_t aIndex);
|
||||
|
||||
void InitNew(CacheFileChunkListener *aCallback);
|
||||
nsresult Read(CacheFileHandle *aHandle, uint32_t aLen,
|
||||
CacheHashUtils::Hash16_t aHash,
|
||||
CacheFileChunkListener *aCallback);
|
||||
nsresult Write(CacheFileHandle *aHandle, CacheFileChunkListener *aCallback);
|
||||
void WaitForUpdate(CacheFileChunkListener *aCallback);
|
||||
nsresult CancelWait(CacheFileChunkListener *aCallback);
|
||||
nsresult NotifyUpdateListeners();
|
||||
|
||||
uint32_t Index();
|
||||
CacheHashUtils::Hash16_t Hash();
|
||||
uint32_t DataSize();
|
||||
void UpdateDataSize(uint32_t aOffset, uint32_t aLen,
|
||||
bool aEOF);
|
||||
|
||||
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
|
||||
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
|
||||
nsresult aResult);
|
||||
NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
|
||||
NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
|
||||
NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult);
|
||||
|
||||
bool IsReady();
|
||||
bool IsDirty();
|
||||
|
||||
char * BufForWriting();
|
||||
const char * BufForReading();
|
||||
void EnsureBufSize(uint32_t aBufSize);
|
||||
uint32_t MemorySize() { return mRWBufSize + mBufSize; }
|
||||
|
||||
private:
|
||||
friend class CacheFileInputStream;
|
||||
friend class CacheFileOutputStream;
|
||||
friend class CacheFile;
|
||||
|
||||
virtual ~CacheFileChunk();
|
||||
|
||||
enum EState {
|
||||
INITIAL = 0,
|
||||
READING = 1,
|
||||
WRITING = 2,
|
||||
READY = 3,
|
||||
ERROR = 4
|
||||
};
|
||||
|
||||
uint32_t mIndex;
|
||||
EState mState;
|
||||
bool mIsDirty;
|
||||
bool mRemovingChunk;
|
||||
uint32_t mDataSize;
|
||||
|
||||
char *mBuf;
|
||||
uint32_t mBufSize;
|
||||
|
||||
char *mRWBuf;
|
||||
uint32_t mRWBufSize;
|
||||
CacheHashUtils::Hash16_t mReadHash;
|
||||
|
||||
nsRefPtr<CacheFile> mFile; // is null if chunk is cached to
|
||||
// prevent reference cycles
|
||||
nsCOMPtr<CacheFileChunkListener> mListener;
|
||||
nsTArray<ChunkListenerItem *> mUpdateListeners;
|
||||
nsTArray<ValidityPair> mValidityMap;
|
||||
};
|
||||
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
1780
netwerk/cache2/CacheFileIOManager.cpp
Normal file
1780
netwerk/cache2/CacheFileIOManager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
227
netwerk/cache2/CacheFileIOManager.h
Normal file
227
netwerk/cache2/CacheFileIOManager.h
Normal file
@ -0,0 +1,227 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CacheFileIOManager__h__
|
||||
#define CacheFileIOManager__h__
|
||||
|
||||
#include "CacheIOThread.h"
|
||||
#include "CacheEntriesEnumerator.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/SHA1.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "pldhash.h"
|
||||
#include "prclist.h"
|
||||
#include "prio.h"
|
||||
|
||||
class nsIFile;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheFileHandle : public nsISupports
|
||||
, public PRCList
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority);
|
||||
bool IsDoomed() { return mIsDoomed; }
|
||||
const SHA1Sum::Hash *Hash() { return mHash; }
|
||||
int64_t FileSize() { return mFileSize; }
|
||||
bool IsPriority() { return mPriority; }
|
||||
bool FileExists() { return mFileExists; }
|
||||
bool IsClosed() { return mClosed; }
|
||||
nsCString & Key() { return mKey; }
|
||||
|
||||
private:
|
||||
friend class CacheFileIOManager;
|
||||
friend class CacheFileHandles;
|
||||
friend class ReleaseNSPRHandleEvent;
|
||||
|
||||
virtual ~CacheFileHandle();
|
||||
|
||||
const SHA1Sum::Hash *mHash;
|
||||
bool mIsDoomed;
|
||||
bool mRemovingHandle;
|
||||
bool mPriority;
|
||||
bool mClosed;
|
||||
bool mInvalid;
|
||||
bool mFileExists; // This means that the file should exists,
|
||||
// but it can be still deleted by OS/user
|
||||
// and then a subsequent OpenNSPRFileDesc()
|
||||
// will fail.
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
int64_t mFileSize;
|
||||
PRFileDesc *mFD; // if null then the file doesn't exists on the disk
|
||||
nsCString mKey;
|
||||
};
|
||||
|
||||
class CacheFileHandles {
|
||||
public:
|
||||
CacheFileHandles();
|
||||
~CacheFileHandles();
|
||||
|
||||
nsresult Init();
|
||||
void Shutdown();
|
||||
|
||||
nsresult GetHandle(const SHA1Sum::Hash *aHash, CacheFileHandle **_retval);
|
||||
nsresult NewHandle(const SHA1Sum::Hash *aHash, bool aPriority, CacheFileHandle **_retval);
|
||||
void RemoveHandle(CacheFileHandle *aHandlle);
|
||||
void GetAllHandles(nsTArray<nsRefPtr<CacheFileHandle> > *_retval);
|
||||
uint32_t HandleCount();
|
||||
|
||||
private:
|
||||
static PLDHashNumber HashKey(PLDHashTable *table, const void *key);
|
||||
static bool MatchEntry(PLDHashTable *table,
|
||||
const PLDHashEntryHdr *entry,
|
||||
const void *key);
|
||||
static void MoveEntry(PLDHashTable *table,
|
||||
const PLDHashEntryHdr *from,
|
||||
PLDHashEntryHdr *to);
|
||||
static void ClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry);
|
||||
|
||||
static PLDHashTableOps mOps;
|
||||
PLDHashTable mTable;
|
||||
bool mInitialized;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class OpenFileEvent;
|
||||
class CloseFileEvent;
|
||||
class ReadEvent;
|
||||
class WriteEvent;
|
||||
|
||||
#define CACHEFILEIOLISTENER_IID \
|
||||
{ /* dcaf2ddc-17cf-4242-bca1-8c86936375a5 */ \
|
||||
0xdcaf2ddc, \
|
||||
0x17cf, \
|
||||
0x4242, \
|
||||
{0xbc, 0xa1, 0x8c, 0x86, 0x93, 0x63, 0x75, 0xa5} \
|
||||
}
|
||||
|
||||
class CacheFileIOListener : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILEIOLISTENER_IID)
|
||||
|
||||
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) = 0;
|
||||
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
|
||||
nsresult aResult) = 0;
|
||||
NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf,
|
||||
nsresult aResult) = 0;
|
||||
NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) = 0;
|
||||
NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileIOListener, CACHEFILEIOLISTENER_IID)
|
||||
|
||||
|
||||
class CacheFileIOManager : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
enum {
|
||||
OPEN = 0U,
|
||||
CREATE = 1U,
|
||||
CREATE_NEW = 2U,
|
||||
PRIORITY = 4U,
|
||||
NOHASH = 8U
|
||||
};
|
||||
|
||||
CacheFileIOManager();
|
||||
|
||||
static nsresult Init();
|
||||
static nsresult Shutdown();
|
||||
static nsresult OnProfile();
|
||||
static already_AddRefed<nsIEventTarget> IOTarget();
|
||||
static already_AddRefed<CacheIOThread> IOThread();
|
||||
|
||||
static nsresult OpenFile(const nsACString &aKey,
|
||||
uint32_t aFlags,
|
||||
CacheFileIOListener *aCallback);
|
||||
static nsresult Read(CacheFileHandle *aHandle, int64_t aOffset,
|
||||
char *aBuf, int32_t aCount,
|
||||
CacheFileIOListener *aCallback);
|
||||
static nsresult Write(CacheFileHandle *aHandle, int64_t aOffset,
|
||||
const char *aBuf, int32_t aCount, bool aValidate,
|
||||
CacheFileIOListener *aCallback);
|
||||
static nsresult DoomFile(CacheFileHandle *aHandle,
|
||||
CacheFileIOListener *aCallback);
|
||||
static nsresult DoomFileByKey(const nsACString &aKey,
|
||||
CacheFileIOListener *aCallback);
|
||||
static nsresult ReleaseNSPRHandle(CacheFileHandle *aHandle);
|
||||
static nsresult TruncateSeekSetEOF(CacheFileHandle *aHandle,
|
||||
int64_t aTruncatePos, int64_t aEOFPos,
|
||||
CacheFileIOListener *aCallback);
|
||||
|
||||
enum EEnumerateMode {
|
||||
ENTRIES,
|
||||
DOOMED
|
||||
};
|
||||
|
||||
static nsresult EnumerateEntryFiles(EEnumerateMode aMode,
|
||||
CacheEntriesEnumerator** aEnumerator);
|
||||
|
||||
private:
|
||||
friend class CacheFileHandle;
|
||||
friend class CacheFileChunk;
|
||||
friend class CacheFile;
|
||||
friend class ShutdownEvent;
|
||||
friend class OpenFileEvent;
|
||||
friend class CloseHandleEvent;
|
||||
friend class ReadEvent;
|
||||
friend class WriteEvent;
|
||||
friend class DoomFileEvent;
|
||||
friend class DoomFileByKeyEvent;
|
||||
friend class ReleaseNSPRHandleEvent;
|
||||
friend class TruncateSeekSetEOFEvent;
|
||||
|
||||
virtual ~CacheFileIOManager();
|
||||
|
||||
static nsresult CloseHandle(CacheFileHandle *aHandle);
|
||||
|
||||
nsresult InitInternal();
|
||||
nsresult ShutdownInternal();
|
||||
|
||||
nsresult OpenFileInternal(const SHA1Sum::Hash *aHash,
|
||||
uint32_t aFlags,
|
||||
CacheFileHandle **_retval);
|
||||
nsresult CloseHandleInternal(CacheFileHandle *aHandle);
|
||||
nsresult ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
|
||||
char *aBuf, int32_t aCount);
|
||||
nsresult WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
|
||||
const char *aBuf, int32_t aCount, bool aValidate);
|
||||
nsresult DoomFileInternal(CacheFileHandle *aHandle);
|
||||
nsresult DoomFileByKeyInternal(const SHA1Sum::Hash *aHash);
|
||||
nsresult ReleaseNSPRHandleInternal(CacheFileHandle *aHandle);
|
||||
nsresult TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
|
||||
int64_t aTruncatePos, int64_t aEOFPos);
|
||||
|
||||
nsresult CreateFile(CacheFileHandle *aHandle);
|
||||
static void GetHashStr(const SHA1Sum::Hash *aHash, nsACString &_retval);
|
||||
nsresult GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval);
|
||||
nsresult GetDoomedFile(nsIFile **_retval);
|
||||
nsresult CheckAndCreateDir(nsIFile *aFile, const char *aDir);
|
||||
nsresult CreateCacheTree();
|
||||
nsresult OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate = false);
|
||||
void NSPRHandleUsed(CacheFileHandle *aHandle);
|
||||
|
||||
|
||||
static CacheFileIOManager *gInstance;
|
||||
bool mShuttingDown;
|
||||
nsRefPtr<CacheIOThread> mIOThread;
|
||||
nsCOMPtr<nsIFile> mCacheDirectory;
|
||||
bool mTreeCreated;
|
||||
CacheFileHandles mHandles;
|
||||
nsTArray<CacheFileHandle *> mHandlesByLastUsed;
|
||||
};
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
634
netwerk/cache2/CacheFileInputStream.cpp
Normal file
634
netwerk/cache2/CacheFileInputStream.cpp
Normal file
@ -0,0 +1,634 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CacheFileInputStream.h"
|
||||
|
||||
#include "CacheLog.h"
|
||||
#include "CacheFile.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ADDREF(CacheFileInputStream)
|
||||
NS_IMETHODIMP_(nsrefcnt)
|
||||
CacheFileInputStream::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
nsrefcnt count = --mRefCnt;
|
||||
NS_LOG_RELEASE(this, count, "CacheFileInputStream");
|
||||
|
||||
if (0 == count) {
|
||||
mRefCnt = 1;
|
||||
delete (this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
mFile->RemoveInput(this);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(CacheFileInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
|
||||
NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
|
||||
NS_INTERFACE_MAP_END_THREADSAFE
|
||||
|
||||
CacheFileInputStream::CacheFileInputStream(CacheFile *aFile)
|
||||
: mFile(aFile)
|
||||
, mPos(0)
|
||||
, mClosed(false)
|
||||
, mStatus(NS_OK)
|
||||
, mWaitingForUpdate(false)
|
||||
, mListeningForChunk(-1)
|
||||
, mInReadSegments(false)
|
||||
, mCallbackFlags(0)
|
||||
{
|
||||
LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
|
||||
MOZ_COUNT_CTOR(CacheFileInputStream);
|
||||
}
|
||||
|
||||
CacheFileInputStream::~CacheFileInputStream()
|
||||
{
|
||||
LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
|
||||
MOZ_COUNT_DTOR(CacheFileInputStream);
|
||||
}
|
||||
|
||||
// nsIInputStream
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::Close()
|
||||
{
|
||||
LOG(("CacheFileInputStream::Close() [this=%p]", this));
|
||||
return CloseWithStatus(NS_OK);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::Available(uint64_t *_retval)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
|
||||
"status=0x%08x]", this, mStatus));
|
||||
return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
EnsureCorrectChunk(false);
|
||||
*_retval = 0;
|
||||
|
||||
if (mChunk) {
|
||||
int64_t canRead;
|
||||
const char *buf;
|
||||
CanRead(&canRead, &buf);
|
||||
|
||||
if (canRead > 0)
|
||||
*_retval = canRead;
|
||||
else if (canRead == 0 && !mFile->mOutput)
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
LOG(("CacheFileInputStream::Available() [this=%p, retval=%lld]",
|
||||
this, *_retval));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::Read(char *aBuf, uint32_t aCount, uint32_t *_retval)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileInputStream::Read() - Stream is closed. [this=%p, "
|
||||
"status=0x%08x]", this, mStatus));
|
||||
|
||||
if NS_FAILED(mStatus)
|
||||
return mStatus;
|
||||
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EnsureCorrectChunk(false);
|
||||
if (!mChunk) {
|
||||
if (mListeningForChunk == -1) {
|
||||
LOG((" no chunk, returning 0 read and NS_OK"));
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
else {
|
||||
LOG((" waiting for chuck, returning WOULD_BLOCK"));
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t canRead;
|
||||
const char *buf;
|
||||
CanRead(&canRead, &buf);
|
||||
|
||||
if (canRead < 0) {
|
||||
// file was truncated ???
|
||||
MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
|
||||
*_retval = 0;
|
||||
rv = NS_OK;
|
||||
}
|
||||
else if (canRead > 0) {
|
||||
*_retval = std::min(static_cast<uint32_t>(canRead), aCount);
|
||||
memcpy(aBuf, buf, *_retval);
|
||||
mPos += *_retval;
|
||||
|
||||
EnsureCorrectChunk(!(canRead < aCount && mPos % kChunkSize == 0));
|
||||
|
||||
rv = NS_OK;
|
||||
}
|
||||
else {
|
||||
if (mFile->mOutput)
|
||||
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
||||
else {
|
||||
*_retval = 0;
|
||||
rv = NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(("CacheFileInputStream::Read() [this=%p, rv=0x%08x, retval=%d",
|
||||
this, rv, *_retval));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
|
||||
uint32_t aCount, uint32_t *_retval)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]",
|
||||
this, aCount));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
|
||||
"status=0x%08x]", this, mStatus));
|
||||
|
||||
if NS_FAILED(mStatus)
|
||||
return mStatus;
|
||||
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EnsureCorrectChunk(false);
|
||||
if (!mChunk) {
|
||||
if (mListeningForChunk == -1) {
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
else {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t canRead;
|
||||
const char *buf;
|
||||
CanRead(&canRead, &buf);
|
||||
|
||||
if (canRead < 0) {
|
||||
// file was truncated ???
|
||||
MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
|
||||
*_retval = 0;
|
||||
rv = NS_OK;
|
||||
}
|
||||
else if (canRead > 0) {
|
||||
uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
|
||||
|
||||
// We need to release the lock to avoid lock re-entering
|
||||
#ifdef DEBUG
|
||||
int64_t oldPos = mPos;
|
||||
#endif
|
||||
mInReadSegments = true;
|
||||
lock.Unlock();
|
||||
rv = aWriter(this, aClosure, buf, 0, toRead, _retval);
|
||||
lock.Lock();
|
||||
mInReadSegments = false;
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(oldPos == mPos);
|
||||
#endif
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(*_retval <= toRead,
|
||||
"writer should not write more than we asked it to write");
|
||||
mPos += *_retval;
|
||||
}
|
||||
|
||||
EnsureCorrectChunk(!(canRead < aCount && mPos % kChunkSize == 0));
|
||||
|
||||
rv = NS_OK;
|
||||
}
|
||||
else {
|
||||
if (mFile->mOutput)
|
||||
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
||||
else {
|
||||
*_retval = 0;
|
||||
rv = NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08x, retval=%d",
|
||||
this, rv, *_retval));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::IsNonBlocking(bool *_retval)
|
||||
{
|
||||
*_retval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAsyncInputStream
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::CloseWithStatus(nsresult aStatus)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
|
||||
this, aStatus));
|
||||
|
||||
if (mClosed) {
|
||||
MOZ_ASSERT(!mCallback);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
|
||||
|
||||
if (mChunk)
|
||||
ReleaseChunk();
|
||||
|
||||
// TODO propagate error from input stream to other streams ???
|
||||
|
||||
MaybeNotifyListener();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
|
||||
uint32_t aFlags,
|
||||
uint32_t aRequestedCount,
|
||||
nsIEventTarget *aEventTarget)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
LOG(("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
|
||||
"requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
|
||||
aRequestedCount, aEventTarget));
|
||||
|
||||
mCallback = aCallback;
|
||||
mCallbackFlags = aFlags;
|
||||
|
||||
if (!mCallback) {
|
||||
if (mWaitingForUpdate) {
|
||||
mChunk->CancelWait(this);
|
||||
mWaitingForUpdate = false;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mClosed) {
|
||||
NotifyListener();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EnsureCorrectChunk(false);
|
||||
|
||||
MaybeNotifyListener();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsISeekableStream
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::Seek(int32_t whence, int64_t offset)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%lld]",
|
||||
this, whence, offset));
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
int64_t newPos = offset;
|
||||
switch (whence) {
|
||||
case NS_SEEK_SET:
|
||||
break;
|
||||
case NS_SEEK_CUR:
|
||||
newPos += mPos;
|
||||
break;
|
||||
case NS_SEEK_END:
|
||||
newPos += mFile->mDataSize;
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("invalid whence");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
mPos = newPos;
|
||||
EnsureCorrectChunk(true);
|
||||
|
||||
LOG(("CacheFileInputStream::Seek() [this=%p, pos=%lld]", this, mPos));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::Tell(int64_t *_retval)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
*_retval = mPos;
|
||||
|
||||
LOG(("CacheFileInputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileInputStream::SetEOF()
|
||||
{
|
||||
MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// CacheFileChunkListener
|
||||
nsresult
|
||||
CacheFileInputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
|
||||
{
|
||||
MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileInputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
|
||||
{
|
||||
MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileInputStream::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
|
||||
CacheFileChunk *aChunk)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08x, "
|
||||
"idx=%d, chunk=%p]", this, aResult, aChunkIdx, aChunk));
|
||||
|
||||
MOZ_ASSERT(mListeningForChunk != -1);
|
||||
|
||||
if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
|
||||
// This is not a chunk that we're waiting for
|
||||
LOG(("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
|
||||
"different chunk. [this=%p, listeningForChunk=%lld]",
|
||||
this, mListeningForChunk));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mChunk);
|
||||
MOZ_ASSERT(!mWaitingForUpdate);
|
||||
mListeningForChunk = -1;
|
||||
|
||||
if (mClosed) {
|
||||
MOZ_ASSERT(!mCallback);
|
||||
|
||||
LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
|
||||
"ignoring notification. [this=%p]", this));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mChunk = aChunk;
|
||||
MaybeNotifyListener();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileInputStream::OnChunkUpdated(CacheFileChunk *aChunk)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
MOZ_ASSERT(!mInReadSegments);
|
||||
|
||||
LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]",
|
||||
this, aChunk->Index()));
|
||||
|
||||
if (!mWaitingForUpdate) {
|
||||
LOG(("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
|
||||
"mWaitingforUpdate == false. [this=%p]", this));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
else {
|
||||
mWaitingForUpdate = false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mChunk == aChunk);
|
||||
|
||||
MaybeNotifyListener();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileInputStream::ReleaseChunk()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]",
|
||||
this, mChunk->Index()));
|
||||
|
||||
if (mWaitingForUpdate) {
|
||||
LOG(("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
|
||||
"[this=%p]", this));
|
||||
|
||||
mChunk->CancelWait(this);
|
||||
mWaitingForUpdate = false;
|
||||
}
|
||||
|
||||
mFile->ReleaseOutsideLock(mChunk.forget().get());
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
|
||||
this, aReleaseOnly));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
uint32_t chunkIdx = mPos / kChunkSize;
|
||||
|
||||
if (mChunk) {
|
||||
if (mChunk->Index() == chunkIdx) {
|
||||
// we have a correct chunk
|
||||
LOG(("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
|
||||
"[this=%p, idx=%d]", this, chunkIdx));
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ReleaseChunk();
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mWaitingForUpdate);
|
||||
|
||||
if (aReleaseOnly)
|
||||
return;
|
||||
|
||||
if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
|
||||
// We're already waiting for this chunk
|
||||
LOG(("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
|
||||
"chunk %lld [this=%p]", mListeningForChunk, this));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
rv = mFile->GetChunkLocked(chunkIdx, false, this, getter_AddRefs(mChunk));
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
|
||||
"[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv));
|
||||
|
||||
}
|
||||
else if (!mChunk) {
|
||||
mListeningForChunk = static_cast<int64_t>(chunkIdx);
|
||||
}
|
||||
|
||||
MaybeNotifyListener();
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileInputStream::CanRead(int64_t *aCanRead, const char **aBuf)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
MOZ_ASSERT(mChunk);
|
||||
MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
|
||||
|
||||
uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
|
||||
*aCanRead = mChunk->DataSize() - chunkOffset;
|
||||
*aBuf = mChunk->BufForReading() + chunkOffset;
|
||||
|
||||
LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%lld]",
|
||||
this, *aCanRead));
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileInputStream::NotifyListener()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
|
||||
|
||||
MOZ_ASSERT(mCallback);
|
||||
|
||||
if (!mCallbackTarget)
|
||||
mCallbackTarget = NS_GetCurrentThread();
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> asyncCallback =
|
||||
NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
|
||||
|
||||
mCallback = nullptr;
|
||||
mCallbackTarget = nullptr;
|
||||
|
||||
asyncCallback->OnInputStreamReady(this);
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileInputStream::MaybeNotifyListener()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
|
||||
"mClosed=%d, mStatus=0x%08x, mChunk=%p, mListeningForChunk=%lld, "
|
||||
"mWaitingForUpdate=%d]", this, mCallback.get(), mClosed, mStatus,
|
||||
mChunk.get(), mListeningForChunk, mWaitingForUpdate));
|
||||
|
||||
if (!mCallback)
|
||||
return;
|
||||
|
||||
if (mClosed) {
|
||||
NotifyListener();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mChunk) {
|
||||
if (mListeningForChunk == -1) {
|
||||
// EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
|
||||
NotifyListener();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
|
||||
|
||||
if (mWaitingForUpdate)
|
||||
return;
|
||||
|
||||
int64_t canRead;
|
||||
const char *buf;
|
||||
CanRead(&canRead, &buf);
|
||||
|
||||
if (canRead > 0) {
|
||||
if (!(mCallbackFlags & WAIT_CLOSURE_ONLY))
|
||||
NotifyListener();
|
||||
}
|
||||
else if (canRead == 0) {
|
||||
if (!mFile->mOutput) {
|
||||
// EOF
|
||||
NotifyListener();
|
||||
}
|
||||
else {
|
||||
mChunk->WaitForUpdate(this);
|
||||
mWaitingForUpdate = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Output have set EOF before mPos?
|
||||
MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
|
||||
NotifyListener();
|
||||
}
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
65
netwerk/cache2/CacheFileInputStream.h
Normal file
65
netwerk/cache2/CacheFileInputStream.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CacheFileInputStream__h__
|
||||
#define CacheFileInputStream__h__
|
||||
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "CacheFileChunk.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheFile;
|
||||
|
||||
class CacheFileInputStream : public nsIAsyncInputStream
|
||||
, public nsISeekableStream
|
||||
, public CacheFileChunkListener
|
||||
{
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
NS_DECL_NSISEEKABLESTREAM
|
||||
|
||||
public:
|
||||
CacheFileInputStream(CacheFile *aFile);
|
||||
|
||||
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
|
||||
CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk);
|
||||
|
||||
private:
|
||||
virtual ~CacheFileInputStream();
|
||||
|
||||
void ReleaseChunk();
|
||||
void EnsureCorrectChunk(bool aReleaseOnly);
|
||||
void CanRead(int64_t *aCanRead, const char **aBuf);
|
||||
void NotifyListener();
|
||||
void MaybeNotifyListener();
|
||||
|
||||
nsRefPtr<CacheFile> mFile;
|
||||
nsRefPtr<CacheFileChunk> mChunk;
|
||||
int64_t mPos;
|
||||
bool mClosed;
|
||||
nsresult mStatus;
|
||||
bool mWaitingForUpdate;
|
||||
int64_t mListeningForChunk;
|
||||
bool mInReadSegments;
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
uint32_t mCallbackFlags;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
||||
};
|
||||
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
682
netwerk/cache2/CacheFileMetadata.cpp
Normal file
682
netwerk/cache2/CacheFileMetadata.cpp
Normal file
@ -0,0 +1,682 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CacheFileMetadata.h"
|
||||
|
||||
#include "CacheLog.h"
|
||||
#include "CacheFileIOManager.h"
|
||||
#include "CacheHashUtils.h"
|
||||
#include "CacheFileChunk.h"
|
||||
#include "../cache/nsCacheUtils.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "prnetdb.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
#define kMinMetadataRead 1024 // TODO find optimal value from telemetry
|
||||
#define kAlignSize 4096
|
||||
|
||||
#define NO_EXPIRATION_TIME 0xFFFFFFFF
|
||||
|
||||
NS_IMPL_ISUPPORTS1(CacheFileMetadata, CacheFileIOListener)
|
||||
|
||||
CacheFileMetadata::CacheFileMetadata(CacheFileHandle *aHandle, const nsACString &aKey, bool aKeyIsHash)
|
||||
: mHandle(aHandle)
|
||||
, mKeyIsHash(aKeyIsHash)
|
||||
, mHashArray(nullptr)
|
||||
, mHashArraySize(0)
|
||||
, mHashCount(0)
|
||||
, mOffset(-1)
|
||||
, mBuf(nullptr)
|
||||
, mBufSize(0)
|
||||
, mWriteBuf(nullptr)
|
||||
, mElementsSize(0)
|
||||
, mIsDirty(false)
|
||||
{
|
||||
LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, handle=%p, key=%s]",
|
||||
this, aHandle, PromiseFlatCString(aKey).get()));
|
||||
|
||||
MOZ_COUNT_CTOR(CacheFileMetadata);
|
||||
memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
|
||||
mMetaHdr.mExpirationTime = NO_EXPIRATION_TIME;
|
||||
mKey = aKey;
|
||||
}
|
||||
|
||||
CacheFileMetadata::~CacheFileMetadata()
|
||||
{
|
||||
LOG(("CacheFileMetadata::~CacheFileMetadata() [this=%p]", this));
|
||||
|
||||
MOZ_COUNT_DTOR(CacheFileMetadata);
|
||||
MOZ_ASSERT(!mListener);
|
||||
|
||||
if (mHashArray) {
|
||||
free(mHashArray);
|
||||
mHashArray = nullptr;
|
||||
mHashArraySize = 0;
|
||||
}
|
||||
|
||||
if (mBuf) {
|
||||
free(mBuf);
|
||||
mBuf = nullptr;
|
||||
mBufSize = 0;
|
||||
}
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
}
|
||||
|
||||
CacheFileMetadata::CacheFileMetadata(const nsACString &aKey)
|
||||
: mHandle(nullptr)
|
||||
, mKeyIsHash(false)
|
||||
, mHashArray(nullptr)
|
||||
, mHashArraySize(0)
|
||||
, mHashCount(0)
|
||||
, mOffset(0)
|
||||
, mBuf(nullptr)
|
||||
, mBufSize(0)
|
||||
, mWriteBuf(nullptr)
|
||||
, mElementsSize(0)
|
||||
, mIsDirty(true)
|
||||
{
|
||||
LOG(("CacheFileMetadata::CacheFileMetadata() [this=%p, key=%s]",
|
||||
this, PromiseFlatCString(aKey).get()));
|
||||
|
||||
MOZ_COUNT_CTOR(CacheFileMetadata);
|
||||
memset(&mMetaHdr, 0, sizeof(CacheFileMetadataHeader));
|
||||
mMetaHdr.mExpirationTime = NO_EXPIRATION_TIME;
|
||||
mMetaHdr.mFetchCount++;
|
||||
mKey = aKey;
|
||||
mMetaHdr.mKeySize = mKey.Length();
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileMetadata::SetHandle(CacheFileHandle *aHandle)
|
||||
{
|
||||
LOG(("CacheFileMetadata::SetHandle() [this=%p, handle=%p]", this, aHandle));
|
||||
|
||||
MOZ_ASSERT(!mHandle);
|
||||
|
||||
mHandle = aHandle;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::ReadMetadata(CacheFileMetadataListener *aListener)
|
||||
{
|
||||
LOG(("CacheFileMetadata::ReadMetadata() [this=%p, listener=%p]", this, aListener));
|
||||
|
||||
MOZ_ASSERT(!mListener);
|
||||
MOZ_ASSERT(!mHashArray);
|
||||
MOZ_ASSERT(!mBuf);
|
||||
MOZ_ASSERT(!mWriteBuf);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
int64_t size = mHandle->FileSize();
|
||||
MOZ_ASSERT(size != -1);
|
||||
|
||||
if (size == 0) {
|
||||
// this is a new entry
|
||||
LOG(("CacheFileMetadata::ReadMetadata() - Filesize == 0, creating empty "
|
||||
"metadata. [this=%p]", this));
|
||||
|
||||
InitEmptyMetadata();
|
||||
aListener->OnMetadataRead(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (size < int64_t(sizeof(CacheFileMetadataHeader) + 2*sizeof(uint32_t))) {
|
||||
// there must be at least checksum, header and offset
|
||||
LOG(("CacheFileMetadata::ReadMetadata() - File is corrupted, creating "
|
||||
"empty metadata. [this=%p, filesize=%lld]", this, size));
|
||||
|
||||
InitEmptyMetadata();
|
||||
aListener->OnMetadataRead(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// round offset to 4k blocks
|
||||
int64_t offset = (size / kAlignSize) * kAlignSize;
|
||||
|
||||
if (size - offset < kMinMetadataRead && offset > kAlignSize)
|
||||
offset -= kAlignSize;
|
||||
|
||||
mBufSize = size - offset;
|
||||
mBuf = static_cast<char *>(moz_xmalloc(mBufSize));
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
|
||||
LOG(("CacheFileMetadata::ReadMetadata() - Reading metadata from disk, trying "
|
||||
"offset=%lld, filesize=%lld [this=%p]", offset, size, this));
|
||||
|
||||
mListener = aListener;
|
||||
rv = CacheFileIOManager::Read(mHandle, offset, mBuf, mBufSize, this);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileMetadata::ReadMetadata() - CacheFileIOManager::Read() failed"
|
||||
" synchronously, creating empty metadata. [this=%p, rv=0x%08x]",
|
||||
this, rv));
|
||||
|
||||
mListener = nullptr;
|
||||
InitEmptyMetadata();
|
||||
aListener->OnMetadataRead(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::WriteMetadata(uint32_t aOffset,
|
||||
CacheFileMetadataListener *aListener)
|
||||
{
|
||||
LOG(("CacheFileMetadata::WriteMetadata() [this=%p, offset=%d, listener=%p]",
|
||||
this, aOffset, aListener));
|
||||
|
||||
MOZ_ASSERT(!mListener);
|
||||
MOZ_ASSERT(!mWriteBuf);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
mIsDirty = false;
|
||||
|
||||
mWriteBuf = static_cast<char *>(moz_xmalloc(sizeof(uint32_t) +
|
||||
mHashCount * sizeof(CacheHashUtils::Hash16_t) +
|
||||
sizeof(CacheFileMetadataHeader) + mKey.Length() + 1 +
|
||||
mElementsSize + sizeof(uint32_t)));
|
||||
|
||||
char *p = mWriteBuf + sizeof(uint32_t);
|
||||
memcpy(p, mHashArray, mHashCount * sizeof(CacheHashUtils::Hash16_t));
|
||||
p += mHashCount * sizeof(CacheHashUtils::Hash16_t);
|
||||
memcpy(p, &mMetaHdr, sizeof(CacheFileMetadataHeader));
|
||||
p += sizeof(CacheFileMetadataHeader);
|
||||
memcpy(p, mKey.get(), mKey.Length());
|
||||
p += mKey.Length();
|
||||
*p = 0;
|
||||
p++;
|
||||
memcpy(p, mBuf, mElementsSize);
|
||||
p += mElementsSize;
|
||||
|
||||
CacheHashUtils::Hash32_t hash;
|
||||
hash = CacheHashUtils::Hash(mWriteBuf + sizeof(uint32_t),
|
||||
p - mWriteBuf - sizeof(uint32_t));
|
||||
*reinterpret_cast<uint32_t *>(mWriteBuf) = PR_htonl(hash);
|
||||
|
||||
*reinterpret_cast<uint32_t *>(p) = PR_htonl(aOffset);
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
mListener = aListener;
|
||||
rv = CacheFileIOManager::Write(mHandle, aOffset, mWriteBuf, p - mWriteBuf,
|
||||
true, this);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() "
|
||||
"failed synchronously. [this=%p, rv=0x%08x]", this, rv));
|
||||
|
||||
mListener = nullptr;
|
||||
free(mWriteBuf);
|
||||
mWriteBuf = nullptr;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const char *
|
||||
CacheFileMetadata::GetElement(const char *aKey)
|
||||
{
|
||||
const char *data = mBuf;
|
||||
const char *limit = mBuf + mElementsSize;
|
||||
|
||||
while (data < limit) {
|
||||
// Point to the value part
|
||||
const char *value = data + strlen(data) + 1;
|
||||
MOZ_ASSERT(value < limit, "Metadata elements corrupted");
|
||||
if (strcmp(data, aKey) == 0) {
|
||||
LOG(("CacheFileMetadata::GetElement() - Key found [this=%p, key=%s]",
|
||||
this, aKey));
|
||||
return value;
|
||||
}
|
||||
|
||||
// Skip value part
|
||||
data = value + strlen(value) + 1;
|
||||
}
|
||||
MOZ_ASSERT(data == limit, "Metadata elements corrupted");
|
||||
LOG(("CacheFileMetadata::GetElement() - Key not found [this=%p, key=%s]",
|
||||
this, aKey));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::SetElement(const char *aKey, const char *aValue)
|
||||
{
|
||||
LOG(("CacheFileMetadata::SetElement() [this=%p, key=%s, value=%p]",
|
||||
this, aKey, aValue));
|
||||
|
||||
MarkDirty();
|
||||
|
||||
const uint32_t keySize = strlen(aKey) + 1;
|
||||
char *pos = const_cast<char *>(GetElement(aKey));
|
||||
|
||||
if (!aValue) {
|
||||
// No value means remove the key/value pair completely, if existing
|
||||
if (pos) {
|
||||
uint32_t oldValueSize = strlen(pos) + 1;
|
||||
uint32_t offset = pos - mBuf;
|
||||
uint32_t remainder = mElementsSize - (offset + oldValueSize);
|
||||
|
||||
memmove(pos - keySize, pos + oldValueSize, remainder);
|
||||
mElementsSize -= keySize + oldValueSize;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const uint32_t valueSize = strlen(aValue) + 1;
|
||||
uint32_t newSize = mElementsSize + valueSize;
|
||||
if (pos) {
|
||||
const uint32_t oldValueSize = strlen(pos) + 1;
|
||||
const uint32_t offset = pos - mBuf;
|
||||
const uint32_t remainder = mElementsSize - (offset + oldValueSize);
|
||||
|
||||
// Update the value in place
|
||||
newSize -= oldValueSize;
|
||||
EnsureBuffer(newSize);
|
||||
|
||||
// Move the remainder to the right place
|
||||
pos = mBuf + offset;
|
||||
memmove(pos + valueSize, pos + oldValueSize, remainder);
|
||||
} else {
|
||||
// allocate new meta data element
|
||||
newSize += keySize;
|
||||
EnsureBuffer(newSize);
|
||||
|
||||
// Add after last element
|
||||
pos = mBuf + mElementsSize;
|
||||
memcpy(pos, aKey, keySize);
|
||||
pos += keySize;
|
||||
}
|
||||
|
||||
// Update value
|
||||
memcpy(pos, aValue, valueSize);
|
||||
mElementsSize = newSize;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CacheHashUtils::Hash16_t
|
||||
CacheFileMetadata::GetHash(uint32_t aIndex)
|
||||
{
|
||||
MOZ_ASSERT(aIndex < mHashCount);
|
||||
return PR_ntohs(mHashArray[aIndex]);
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::SetHash(uint32_t aIndex, CacheHashUtils::Hash16_t aHash)
|
||||
{
|
||||
LOG(("CacheFileMetadata::SetHash() [this=%p, idx=%d, hash=%x]",
|
||||
this, aIndex, aHash));
|
||||
|
||||
MarkDirty();
|
||||
|
||||
MOZ_ASSERT(aIndex <= mHashCount);
|
||||
|
||||
if (aIndex > mHashCount) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
} else if (aIndex == mHashCount) {
|
||||
if ((aIndex + 1) * sizeof(CacheHashUtils::Hash16_t) > mHashArraySize) {
|
||||
// reallocate hash array buffer
|
||||
if (mHashArraySize == 0)
|
||||
mHashArraySize = 32 * sizeof(CacheHashUtils::Hash16_t);
|
||||
else
|
||||
mHashArraySize *= 2;
|
||||
mHashArray = static_cast<CacheHashUtils::Hash16_t *>(
|
||||
moz_xrealloc(mHashArray, mHashArraySize));
|
||||
}
|
||||
|
||||
mHashCount++;
|
||||
}
|
||||
|
||||
mHashArray[aIndex] = PR_htons(aHash);
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::SetExpirationTime(uint32_t aExpirationTime)
|
||||
{
|
||||
LOG(("CacheFileMetadata::SetExpirationTime() [this=%p, expirationTime=%d]",
|
||||
this, aExpirationTime));
|
||||
|
||||
MarkDirty();
|
||||
mMetaHdr.mExpirationTime = aExpirationTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::GetExpirationTime(uint32_t *_retval)
|
||||
{
|
||||
*_retval = mMetaHdr.mExpirationTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::SetLastModified(uint32_t aLastModified)
|
||||
{
|
||||
LOG(("CacheFileMetadata::SetLastModified() [this=%p, lastModified=%d]",
|
||||
this, aLastModified));
|
||||
|
||||
MarkDirty();
|
||||
mMetaHdr.mLastModified = aLastModified;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::GetLastModified(uint32_t *_retval)
|
||||
{
|
||||
*_retval = mMetaHdr.mLastModified;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::GetLastFetched(uint32_t *_retval)
|
||||
{
|
||||
*_retval = mMetaHdr.mLastFetched;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::GetFetchCount(uint32_t *_retval)
|
||||
{
|
||||
*_retval = mMetaHdr.mFetchCount;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
|
||||
{
|
||||
MOZ_CRASH("CacheFileMetadata::OnFileOpened should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
|
||||
nsresult aResult)
|
||||
{
|
||||
LOG(("CacheFileMetadata::OnDataWritten() [this=%p, handle=%p, result=0x%08x]",
|
||||
this, aHandle, aResult));
|
||||
|
||||
MOZ_ASSERT(mListener);
|
||||
MOZ_ASSERT(mWriteBuf);
|
||||
|
||||
free(mWriteBuf);
|
||||
mWriteBuf = nullptr;
|
||||
|
||||
nsCOMPtr<CacheFileMetadataListener> listener;
|
||||
|
||||
mListener.swap(listener);
|
||||
listener->OnMetadataWritten(aResult);
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
|
||||
nsresult aResult)
|
||||
{
|
||||
LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
|
||||
this, aHandle, aResult));
|
||||
|
||||
MOZ_ASSERT(mListener);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<CacheFileMetadataListener> listener;
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed, "
|
||||
"creating empty metadata. [this=%p, rv=0x%08x]", this, aResult));
|
||||
|
||||
InitEmptyMetadata();
|
||||
mListener.swap(listener);
|
||||
listener->OnMetadataRead(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// check whether we have read all necessary data
|
||||
uint32_t realOffset = PR_ntohl(*(reinterpret_cast<uint32_t *>(
|
||||
mBuf + mBufSize - sizeof(uint32_t))));
|
||||
|
||||
int64_t size = mHandle->FileSize();
|
||||
MOZ_ASSERT(size != -1);
|
||||
|
||||
if (realOffset >= size) {
|
||||
LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating empty "
|
||||
"metadata. [this=%p, realOffset=%d, size=%lld]", this, realOffset,
|
||||
size));
|
||||
|
||||
InitEmptyMetadata();
|
||||
mListener.swap(listener);
|
||||
listener->OnMetadataRead(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t usedOffset = size - mBufSize;
|
||||
|
||||
if (realOffset < usedOffset) {
|
||||
uint32_t missing = usedOffset - realOffset;
|
||||
// we need to read more data
|
||||
mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize + missing));
|
||||
memmove(mBuf + missing, mBuf, mBufSize);
|
||||
mBufSize += missing;
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
|
||||
LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to "
|
||||
"have full metadata. [this=%p]", missing, this));
|
||||
|
||||
rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, this);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed"
|
||||
" synchronously, creating empty metadata. [this=%p, rv=0x%08x]",
|
||||
this, rv));
|
||||
|
||||
InitEmptyMetadata();
|
||||
mListener.swap(listener);
|
||||
listener->OnMetadataRead(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We have all data according to offset information at the end of the entry.
|
||||
// Try to parse it.
|
||||
rv = ParseMetadata(realOffset, realOffset - usedOffset);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating "
|
||||
"empty metadata. [this=%p]", this));
|
||||
InitEmptyMetadata();
|
||||
}
|
||||
|
||||
mListener.swap(listener);
|
||||
listener->OnMetadataRead(NS_OK);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
|
||||
{
|
||||
MOZ_CRASH("CacheFileMetadata::OnFileDoomed should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
|
||||
{
|
||||
MOZ_CRASH("CacheFileMetadata::OnEOFSet should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileMetadata::InitEmptyMetadata()
|
||||
{
|
||||
if (mBuf) {
|
||||
free(mBuf);
|
||||
mBuf = nullptr;
|
||||
mBufSize = 0;
|
||||
}
|
||||
mOffset = 0;
|
||||
mMetaHdr.mFetchCount = 1;
|
||||
mMetaHdr.mExpirationTime = NO_EXPIRATION_TIME;
|
||||
mMetaHdr.mKeySize = mKey.Length();
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset)
|
||||
{
|
||||
LOG(("CacheFileMetadata::ParseMetadata() [this=%p, metaOffset=%d, "
|
||||
"bufOffset=%d]", this, aMetaOffset, aBufOffset));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
uint32_t metaposOffset = mBufSize - sizeof(uint32_t);
|
||||
uint32_t hashesOffset = aBufOffset + sizeof(uint32_t);
|
||||
uint32_t hashCount = aMetaOffset / kChunkSize;
|
||||
if (aMetaOffset % kChunkSize)
|
||||
hashCount++;
|
||||
uint32_t hashesLen = hashCount * sizeof(CacheHashUtils::Hash16_t);
|
||||
uint32_t hdrOffset = hashesOffset + hashesLen;
|
||||
uint32_t keyOffset = hdrOffset + sizeof(CacheFileMetadataHeader);
|
||||
|
||||
LOG(("CacheFileMetadata::ParseMetadata() [this=%p]\n metaposOffset=%d\n "
|
||||
"hashesOffset=%d\n hashCount=%d\n hashesLen=%d\n hdfOffset=%d\n "
|
||||
"keyOffset=%d\n", this, metaposOffset, hashesOffset, hashCount,
|
||||
hashesLen,hdrOffset, keyOffset));
|
||||
|
||||
if (keyOffset > metaposOffset) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - Wrong keyOffset! [this=%p]",
|
||||
this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
uint32_t elementsOffset = reinterpret_cast<CacheFileMetadataHeader *>(
|
||||
mBuf + hdrOffset)->mKeySize + keyOffset + 1;
|
||||
|
||||
if (elementsOffset > metaposOffset) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - Wrong elementsOffset %d "
|
||||
"[this=%p]", elementsOffset, this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
// check that key ends with \0
|
||||
if (mBuf[elementsOffset - 1] != 0) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - Elements not null terminated. "
|
||||
"[this=%p]", this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
if (!mKeyIsHash) {
|
||||
uint32_t keySize = reinterpret_cast<CacheFileMetadataHeader *>(
|
||||
mBuf + hdrOffset)->mKeySize;
|
||||
|
||||
if (keySize != mKey.Length()) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - Key collision (1), key=%s [this=%p]",
|
||||
nsCString(mBuf + keyOffset, keySize).get(), this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
if (memcmp(mKey.get(), mBuf + keyOffset, mKey.Length()) != 0) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - Key collision (2), key=%s [this=%p]",
|
||||
nsCString(mBuf + keyOffset, keySize).get(), this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
}
|
||||
|
||||
// check metadata hash (data from hashesOffset to metaposOffset)
|
||||
CacheHashUtils::Hash32_t hash;
|
||||
hash = CacheHashUtils::Hash(mBuf + hashesOffset,
|
||||
metaposOffset - hashesOffset);
|
||||
|
||||
if (hash != PR_ntohl(*(reinterpret_cast<uint32_t *>(mBuf + aBufOffset)))) {
|
||||
LOG(("CacheFileMetadata::ParseMetadata() - Metadata hash mismatch! Hash of "
|
||||
"the metadata is %x, hash in file is %x [this=%p]", hash,
|
||||
PR_ntohl(*(reinterpret_cast<uint32_t *>(mBuf + aBufOffset))), this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
// check elements
|
||||
rv = CheckElements(mBuf + elementsOffset, metaposOffset - elementsOffset);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
mHashArraySize = hashesLen;
|
||||
mHashCount = hashCount;
|
||||
if (mHashArraySize) {
|
||||
mHashArray = static_cast<CacheHashUtils::Hash16_t *>(
|
||||
moz_xmalloc(mHashArraySize));
|
||||
memcpy(mHashArray, mBuf + hashesOffset, mHashArraySize);
|
||||
}
|
||||
|
||||
memcpy(&mMetaHdr, mBuf + hdrOffset, sizeof(CacheFileMetadataHeader));
|
||||
mMetaHdr.mFetchCount++;
|
||||
MarkDirty();
|
||||
|
||||
mElementsSize = metaposOffset - elementsOffset;
|
||||
memmove(mBuf, mBuf + elementsOffset, mElementsSize);
|
||||
mOffset = aMetaOffset;
|
||||
|
||||
// TODO: shrink memory if buffer is too big
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileMetadata::CheckElements(const char *aBuf, uint32_t aSize)
|
||||
{
|
||||
if (aSize) {
|
||||
// Check if the metadata ends with a zero byte.
|
||||
if (aBuf[aSize - 1] != 0) {
|
||||
NS_ERROR("Metadata elements are not null terminated");
|
||||
LOG(("CacheFileMetadata::CheckElements() - Elements are not null "
|
||||
"terminated. [this=%p]", this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
// Check that there are an even number of zero bytes
|
||||
// to match the pattern { key \0 value \0 }
|
||||
bool odd = false;
|
||||
for (uint32_t i = 0; i < aSize; i++) {
|
||||
if (aBuf[i] == 0)
|
||||
odd = !odd;
|
||||
}
|
||||
if (odd) {
|
||||
NS_ERROR("Metadata elements are malformed");
|
||||
LOG(("CacheFileMetadata::CheckElements() - Elements are malformed. "
|
||||
"[this=%p]", this));
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileMetadata::EnsureBuffer(uint32_t aSize)
|
||||
{
|
||||
if (mBufSize < aSize) {
|
||||
mBufSize = aSize;
|
||||
mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize));
|
||||
}
|
||||
|
||||
DoMemoryReport(MemoryUsage());
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
119
netwerk/cache2/CacheFileMetadata.h
Normal file
119
netwerk/cache2/CacheFileMetadata.h
Normal file
@ -0,0 +1,119 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CacheFileMetadata__h__
|
||||
#define CacheFileMetadata__h__
|
||||
|
||||
#include "CacheFileIOManager.h"
|
||||
#include "CacheStorageService.h"
|
||||
#include "CacheHashUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
typedef struct {
|
||||
uint32_t mFetchCount;
|
||||
uint32_t mLastFetched;
|
||||
uint32_t mLastModified;
|
||||
uint32_t mExpirationTime;
|
||||
uint32_t mKeySize;
|
||||
} CacheFileMetadataHeader;
|
||||
|
||||
#define CACHEFILEMETADATALISTENER_IID \
|
||||
{ /* a9e36125-3f01-4020-9540-9dafa8d31ba7 */ \
|
||||
0xa9e36125, \
|
||||
0x3f01, \
|
||||
0x4020, \
|
||||
{0x95, 0x40, 0x9d, 0xaf, 0xa8, 0xd3, 0x1b, 0xa7} \
|
||||
}
|
||||
|
||||
class CacheFileMetadataListener : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(CACHEFILEMETADATALISTENER_IID)
|
||||
|
||||
NS_IMETHOD OnMetadataRead(nsresult aResult) = 0;
|
||||
NS_IMETHOD OnMetadataWritten(nsresult aResult) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileMetadataListener,
|
||||
CACHEFILEMETADATALISTENER_IID)
|
||||
|
||||
|
||||
class CacheFileMetadata : public CacheFileIOListener
|
||||
, public CacheMemoryConsumer
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
CacheFileMetadata(CacheFileHandle *aHandle,
|
||||
const nsACString &aKey,
|
||||
bool aKeyIsHash);
|
||||
CacheFileMetadata(const nsACString &aKey);
|
||||
|
||||
void SetHandle(CacheFileHandle *aHandle);
|
||||
|
||||
nsresult ReadMetadata(CacheFileMetadataListener *aListener);
|
||||
nsresult WriteMetadata(uint32_t aOffset,
|
||||
CacheFileMetadataListener *aListener);
|
||||
|
||||
const char * GetElement(const char *aKey);
|
||||
nsresult SetElement(const char *aKey, const char *aValue);
|
||||
|
||||
CacheHashUtils::Hash16_t GetHash(uint32_t aIndex);
|
||||
nsresult SetHash(uint32_t aIndex,
|
||||
CacheHashUtils::Hash16_t aHash);
|
||||
|
||||
nsresult SetExpirationTime(uint32_t aExpirationTime);
|
||||
nsresult GetExpirationTime(uint32_t *_retval);
|
||||
nsresult SetLastModified(uint32_t aLastModified);
|
||||
nsresult GetLastModified(uint32_t *_retval);
|
||||
nsresult GetLastFetched(uint32_t *_retval);
|
||||
nsresult GetFetchCount(uint32_t *_retval);
|
||||
|
||||
int64_t Offset() { return mOffset; }
|
||||
uint32_t ElementsSize() { return mElementsSize; }
|
||||
void MarkDirty() { mIsDirty = true; }
|
||||
bool IsDirty() { return mIsDirty; }
|
||||
uint32_t MemoryUsage() { return mHashArraySize + mBufSize; }
|
||||
|
||||
NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult);
|
||||
NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
|
||||
nsresult aResult);
|
||||
NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult);
|
||||
NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult);
|
||||
NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult);
|
||||
|
||||
private:
|
||||
virtual ~CacheFileMetadata();
|
||||
|
||||
void InitEmptyMetadata();
|
||||
nsresult ParseMetadata(uint32_t aMetaOffset, uint32_t aBufOffset);
|
||||
nsresult CheckElements(const char *aBuf, uint32_t aSize);
|
||||
void EnsureBuffer(uint32_t aSize);
|
||||
|
||||
nsRefPtr<CacheFileHandle> mHandle;
|
||||
nsCString mKey;
|
||||
bool mKeyIsHash;
|
||||
CacheHashUtils::Hash16_t *mHashArray;
|
||||
uint32_t mHashArraySize;
|
||||
uint32_t mHashCount;
|
||||
int64_t mOffset;
|
||||
char *mBuf; // used for parsing, then points
|
||||
// to elements
|
||||
uint32_t mBufSize;
|
||||
char *mWriteBuf;
|
||||
CacheFileMetadataHeader mMetaHdr;
|
||||
uint32_t mElementsSize;
|
||||
bool mIsDirty;
|
||||
nsCOMPtr<CacheFileMetadataListener> mListener;
|
||||
};
|
||||
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
382
netwerk/cache2/CacheFileOutputStream.cpp
Normal file
382
netwerk/cache2/CacheFileOutputStream.cpp
Normal file
@ -0,0 +1,382 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CacheFileOutputStream.h"
|
||||
|
||||
#include "CacheLog.h"
|
||||
#include "CacheFile.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ADDREF(CacheFileOutputStream)
|
||||
NS_IMETHODIMP_(nsrefcnt)
|
||||
CacheFileOutputStream::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
nsrefcnt count = --mRefCnt;
|
||||
NS_LOG_RELEASE(this, count, "CacheFileOutputStream");
|
||||
|
||||
if (0 == count) {
|
||||
mRefCnt = 1;
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
mFile->RemoveOutput(this);
|
||||
}
|
||||
delete (this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
|
||||
NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
|
||||
NS_INTERFACE_MAP_END_THREADSAFE
|
||||
|
||||
CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile)
|
||||
: mFile(aFile)
|
||||
, mPos(0)
|
||||
, mClosed(false)
|
||||
, mStatus(NS_OK)
|
||||
, mCallbackFlags(0)
|
||||
{
|
||||
LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
|
||||
MOZ_COUNT_CTOR(CacheFileOutputStream);
|
||||
}
|
||||
|
||||
CacheFileOutputStream::~CacheFileOutputStream()
|
||||
{
|
||||
LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
|
||||
MOZ_COUNT_DTOR(CacheFileOutputStream);
|
||||
}
|
||||
|
||||
// nsIOutputStream
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::Close()
|
||||
{
|
||||
LOG(("CacheFileOutputStream::Close() [this=%p]", this));
|
||||
return CloseWithStatus(NS_OK);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::Flush()
|
||||
{
|
||||
// TODO do we need to implement flush ???
|
||||
LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::Write(const char * aBuf, uint32_t aCount,
|
||||
uint32_t *_retval)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
|
||||
LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
|
||||
"status=0x%08x]", this, mStatus));
|
||||
|
||||
return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
*_retval = aCount;
|
||||
|
||||
while (aCount) {
|
||||
EnsureCorrectChunk(false);
|
||||
|
||||
FillHole();
|
||||
|
||||
uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
|
||||
uint32_t canWrite = kChunkSize - chunkOffset;
|
||||
uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount);
|
||||
mChunk->EnsureBufSize(chunkOffset + thisWrite);
|
||||
memcpy(mChunk->BufForWriting() + chunkOffset, aBuf, thisWrite);
|
||||
|
||||
mPos += thisWrite;
|
||||
aBuf += thisWrite;
|
||||
aCount -= thisWrite;
|
||||
|
||||
mChunk->UpdateDataSize(chunkOffset, thisWrite, false);
|
||||
}
|
||||
|
||||
EnsureCorrectChunk(true);
|
||||
|
||||
LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]",
|
||||
*_retval, this));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount,
|
||||
uint32_t *_retval)
|
||||
{
|
||||
LOG(("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
|
||||
", count=%d]", this, aFromStream, aCount));
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure,
|
||||
uint32_t aCount, uint32_t *_retval)
|
||||
{
|
||||
LOG(("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
|
||||
"count=%d]", this, aCount));
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::IsNonBlocking(bool *_retval)
|
||||
{
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAsyncOutputStream
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::CloseWithStatus(nsresult aStatus)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
|
||||
LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
|
||||
this, aStatus));
|
||||
|
||||
if (mClosed) {
|
||||
MOZ_ASSERT(!mCallback);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
|
||||
|
||||
if (mChunk)
|
||||
ReleaseChunk();
|
||||
|
||||
if (mCallback)
|
||||
NotifyListener();
|
||||
|
||||
mFile->RemoveOutput(this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback *aCallback,
|
||||
uint32_t aFlags,
|
||||
uint32_t aRequestedCount,
|
||||
nsIEventTarget *aEventTarget)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
|
||||
LOG(("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
|
||||
"requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
|
||||
aRequestedCount, aEventTarget));
|
||||
|
||||
mCallback = aCallback;
|
||||
mCallbackFlags = aFlags;
|
||||
|
||||
if (!mCallback)
|
||||
return NS_OK;
|
||||
|
||||
// The stream is blocking so it is writable at any time
|
||||
if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY))
|
||||
NotifyListener();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsISeekableStream
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::Seek(int32_t whence, int64_t offset)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
|
||||
LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
|
||||
this, whence, offset));
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
int64_t newPos = offset;
|
||||
switch (whence) {
|
||||
case NS_SEEK_SET:
|
||||
break;
|
||||
case NS_SEEK_CUR:
|
||||
newPos += mPos;
|
||||
break;
|
||||
case NS_SEEK_END:
|
||||
newPos += mFile->mDataSize;
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("invalid whence");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
mPos = newPos;
|
||||
EnsureCorrectChunk(true);
|
||||
|
||||
LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%lld]", this, mPos));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::Tell(int64_t *_retval)
|
||||
{
|
||||
CacheFileAutoLock lock(mFile);
|
||||
|
||||
if (mClosed) {
|
||||
LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
*_retval = mPos;
|
||||
|
||||
LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CacheFileOutputStream::SetEOF()
|
||||
{
|
||||
MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
|
||||
// Right now we don't use SetEOF(). If we ever need this method, we need
|
||||
// to think about what to do with input streams that already points beyond
|
||||
// new EOF.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// CacheFileChunkListener
|
||||
nsresult
|
||||
CacheFileOutputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
|
||||
{
|
||||
MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileOutputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
|
||||
{
|
||||
MOZ_CRASH(
|
||||
"CacheFileOutputStream::OnChunkWritten should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileOutputStream::OnChunkAvailable(nsresult aResult,
|
||||
uint32_t aChunkIdx,
|
||||
CacheFileChunk *aChunk)
|
||||
{
|
||||
MOZ_CRASH(
|
||||
"CacheFileOutputStream::OnChunkAvailable should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CacheFileOutputStream::OnChunkUpdated(CacheFileChunk *aChunk)
|
||||
{
|
||||
MOZ_CRASH(
|
||||
"CacheFileOutputStream::OnChunkUpdated should not be called!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileOutputStream::ReleaseChunk()
|
||||
{
|
||||
LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
|
||||
this, mChunk->Index()));
|
||||
|
||||
mFile->ReleaseOutsideLock(mChunk.forget().get());
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly)
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
|
||||
this, aReleaseOnly));
|
||||
|
||||
uint32_t chunkIdx = mPos / kChunkSize;
|
||||
|
||||
if (mChunk) {
|
||||
if (mChunk->Index() == chunkIdx) {
|
||||
// we have a correct chunk
|
||||
LOG(("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
|
||||
"[this=%p, idx=%d]", this, chunkIdx));
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ReleaseChunk();
|
||||
}
|
||||
}
|
||||
|
||||
if (aReleaseOnly)
|
||||
return;
|
||||
|
||||
DebugOnly<nsresult> rv;
|
||||
rv = mFile->GetChunkLocked(chunkIdx, true, nullptr, getter_AddRefs(mChunk));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv),
|
||||
"CacheFile::GetChunkLocked() should always succeed for writer");
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileOutputStream::FillHole()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
MOZ_ASSERT(mChunk);
|
||||
MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
|
||||
|
||||
uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize;
|
||||
if (mChunk->DataSize() >= pos)
|
||||
return;
|
||||
|
||||
LOG(("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
|
||||
"%d-%d [this=%p]", mChunk->Index(), mChunk->DataSize(), pos - 1, this));
|
||||
|
||||
mChunk->EnsureBufSize(pos);
|
||||
memset(mChunk->BufForWriting() + mChunk->DataSize(), 0,
|
||||
pos - mChunk->DataSize());
|
||||
|
||||
mChunk->UpdateDataSize(mChunk->DataSize(), pos - mChunk->DataSize(), false);
|
||||
}
|
||||
|
||||
void
|
||||
CacheFileOutputStream::NotifyListener()
|
||||
{
|
||||
mFile->AssertOwnsLock();
|
||||
|
||||
LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
|
||||
|
||||
MOZ_ASSERT(mCallback);
|
||||
|
||||
if (!mCallbackTarget)
|
||||
mCallbackTarget = NS_GetCurrentThread();
|
||||
|
||||
nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
|
||||
NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
|
||||
|
||||
mCallback = nullptr;
|
||||
mCallbackTarget = nullptr;
|
||||
|
||||
asyncCallback->OnOutputStreamReady(this);
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
61
netwerk/cache2/CacheFileOutputStream.h
Normal file
61
netwerk/cache2/CacheFileOutputStream.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CacheFileOutputStream__h__
|
||||
#define CacheFileOutputStream__h__
|
||||
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "CacheFileChunk.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheFile;
|
||||
|
||||
class CacheFileOutputStream : public nsIAsyncOutputStream
|
||||
, public nsISeekableStream
|
||||
, public CacheFileChunkListener
|
||||
{
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOUTPUTSTREAM
|
||||
NS_DECL_NSIASYNCOUTPUTSTREAM
|
||||
NS_DECL_NSISEEKABLESTREAM
|
||||
|
||||
public:
|
||||
CacheFileOutputStream(CacheFile *aFile);
|
||||
|
||||
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
|
||||
CacheFileChunk *aChunk);
|
||||
NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk);
|
||||
|
||||
private:
|
||||
virtual ~CacheFileOutputStream();
|
||||
|
||||
void ReleaseChunk();
|
||||
void EnsureCorrectChunk(bool aReleaseOnly);
|
||||
void FillHole();
|
||||
void NotifyListener();
|
||||
|
||||
nsRefPtr<CacheFile> mFile;
|
||||
nsRefPtr<CacheFileChunk> mChunk;
|
||||
int64_t mPos;
|
||||
bool mClosed;
|
||||
nsresult mStatus;
|
||||
|
||||
nsCOMPtr<nsIOutputStreamCallback> mCallback;
|
||||
uint32_t mCallbackFlags;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
||||
};
|
||||
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
87
netwerk/cache2/CacheHashUtils.cpp
Normal file
87
netwerk/cache2/CacheHashUtils.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CacheHashUtils.h"
|
||||
|
||||
#include "plstr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
/**
|
||||
* CacheHashUtils::Hash(const char * key, uint32_t initval)
|
||||
*
|
||||
* See http://burtleburtle.net/bob/hash/evahash.html for more information
|
||||
* about this hash function.
|
||||
*
|
||||
* This algorithm is used to check the data integrity.
|
||||
*/
|
||||
|
||||
static inline void hashmix(uint32_t& a, uint32_t& b, uint32_t& c)
|
||||
{
|
||||
a -= b; a -= c; a ^= (c>>13);
|
||||
b -= c; b -= a; b ^= (a<<8);
|
||||
c -= a; c -= b; c ^= (b>>13);
|
||||
a -= b; a -= c; a ^= (c>>12);
|
||||
b -= c; b -= a; b ^= (a<<16);
|
||||
c -= a; c -= b; c ^= (b>>5);
|
||||
a -= b; a -= c; a ^= (c>>3);
|
||||
b -= c; b -= a; b ^= (a<<10);
|
||||
c -= a; c -= b; c ^= (b>>15);
|
||||
}
|
||||
|
||||
CacheHashUtils::Hash32_t
|
||||
CacheHashUtils::Hash(const char *aData, uint32_t aSize, uint32_t aInitval)
|
||||
{
|
||||
const uint8_t *k = reinterpret_cast<const uint8_t*>(aData);
|
||||
uint32_t a, b, c, len/*, length*/;
|
||||
|
||||
// length = PL_strlen(key);
|
||||
/* Set up the internal state */
|
||||
len = aSize;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = aInitval; /* variable initialization of internal state */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 12)
|
||||
{
|
||||
a += k[0] + (uint32_t(k[1])<<8) + (uint32_t(k[2])<<16) + (uint32_t(k[3])<<24);
|
||||
b += k[4] + (uint32_t(k[5])<<8) + (uint32_t(k[6])<<16) + (uint32_t(k[7])<<24);
|
||||
c += k[8] + (uint32_t(k[9])<<8) + (uint32_t(k[10])<<16) + (uint32_t(k[11])<<24);
|
||||
hashmix(a, b, c);
|
||||
k += 12; len -= 12;
|
||||
}
|
||||
|
||||
/*------------------------------------- handle the last 11 bytes */
|
||||
c += aSize;
|
||||
switch(len) { /* all the case statements fall through */
|
||||
case 11: c += (uint32_t(k[10])<<24);
|
||||
case 10: c += (uint32_t(k[9])<<16);
|
||||
case 9 : c += (uint32_t(k[8])<<8);
|
||||
/* the low-order byte of c is reserved for the length */
|
||||
case 8 : b += (uint32_t(k[7])<<24);
|
||||
case 7 : b += (uint32_t(k[6])<<16);
|
||||
case 6 : b += (uint32_t(k[5])<<8);
|
||||
case 5 : b += k[4];
|
||||
case 4 : a += (uint32_t(k[3])<<24);
|
||||
case 3 : a += (uint32_t(k[2])<<16);
|
||||
case 2 : a += (uint32_t(k[1])<<8);
|
||||
case 1 : a += k[0];
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
hashmix(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
CacheHashUtils::Hash16_t
|
||||
CacheHashUtils::Hash16(const char *aData, uint32_t aSize, uint32_t aInitval)
|
||||
{
|
||||
Hash32_t hash = Hash(aData, aSize, aInitval);
|
||||
return (hash & 0xFFFF);
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
40
netwerk/cache2/CacheHashUtils.h
Normal file
40
netwerk/cache2/CacheHashUtils.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CacheHashUtils__h__
|
||||
#define CacheHashUtils__h__
|
||||
|
||||
#include "mozilla/Types.h"
|
||||
#include "prnetdb.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#define LOGSHA1(x) \
|
||||
PR_htonl((reinterpret_cast<const uint32_t *>(x))[0]), \
|
||||
PR_htonl((reinterpret_cast<const uint32_t *>(x))[1]), \
|
||||
PR_htonl((reinterpret_cast<const uint32_t *>(x))[2]), \
|
||||
PR_htonl((reinterpret_cast<const uint32_t *>(x))[3]), \
|
||||
PR_htonl((reinterpret_cast<const uint32_t *>(x))[4])
|
||||
|
||||
#define SHA1STRING(x) \
|
||||
(nsPrintfCString("%08x%08x%08x%08x%08x", LOGSHA1(x)).get())
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheHashUtils
|
||||
{
|
||||
public:
|
||||
typedef uint16_t Hash16_t;
|
||||
typedef uint32_t Hash32_t;
|
||||
|
||||
static Hash32_t Hash(const char* aData, uint32_t aSize, uint32_t aInitval=0);
|
||||
static Hash16_t Hash16(const char* aData, uint32_t aSize,
|
||||
uint32_t aInitval=0);
|
||||
};
|
||||
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user