Bug 913807 - HTTP cache v2: file I/O, off by default, r=honzab

This commit is contained in:
Michal Novotny 2013-09-20 11:11:26 +02:00
parent 894ba59236
commit f3cf185b1e
14 changed files with 6746 additions and 0 deletions

1634
netwerk/cache2/CacheFile.cpp Normal file

File diff suppressed because it is too large Load Diff

217
netwerk/cache2/CacheFile.h Normal file
View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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