michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "CacheLog.h" michael@0: #include "CacheFileChunk.h" michael@0: michael@0: #include "CacheFile.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsAlgorithm.h" michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: #define kMinBufSize 512 michael@0: michael@0: class NotifyUpdateListenerEvent : public nsRunnable { michael@0: public: michael@0: NotifyUpdateListenerEvent(CacheFileChunkListener *aCallback, michael@0: CacheFileChunk *aChunk) michael@0: : mCallback(aCallback) michael@0: , mChunk(aChunk) michael@0: { michael@0: LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]", michael@0: this)); michael@0: MOZ_COUNT_CTOR(NotifyUpdateListenerEvent); michael@0: } michael@0: michael@0: ~NotifyUpdateListenerEvent() michael@0: { michael@0: LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]", michael@0: this)); michael@0: MOZ_COUNT_DTOR(NotifyUpdateListenerEvent); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this)); michael@0: michael@0: mCallback->OnChunkUpdated(mChunk); michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsCOMPtr mCallback; michael@0: nsRefPtr mChunk; michael@0: }; michael@0: michael@0: michael@0: class ValidityPair { michael@0: public: michael@0: ValidityPair(uint32_t aOffset, uint32_t aLen) michael@0: : mOffset(aOffset), mLen(aLen) michael@0: {} michael@0: michael@0: ValidityPair& operator=(const ValidityPair& aOther) { michael@0: mOffset = aOther.mOffset; michael@0: mLen = aOther.mLen; michael@0: return *this; michael@0: } michael@0: michael@0: bool Overlaps(const ValidityPair& aOther) const { michael@0: if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) || michael@0: (aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset)) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool LessThan(const ValidityPair& aOther) const { michael@0: if (mOffset < aOther.mOffset) michael@0: return true; michael@0: michael@0: if (mOffset == aOther.mOffset && mLen < aOther.mLen) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void Merge(const ValidityPair& aOther) { michael@0: MOZ_ASSERT(Overlaps(aOther)); michael@0: michael@0: uint32_t offset = std::min(mOffset, aOther.mOffset); michael@0: uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen); michael@0: michael@0: mOffset = offset; michael@0: mLen = end - offset; michael@0: } michael@0: michael@0: uint32_t Offset() { return mOffset; } michael@0: uint32_t Len() { return mLen; } michael@0: michael@0: private: michael@0: uint32_t mOffset; michael@0: uint32_t mLen; michael@0: }; michael@0: michael@0: michael@0: NS_IMPL_ADDREF(CacheFileChunk) michael@0: NS_IMETHODIMP_(MozExternalRefCountType) michael@0: CacheFileChunk::Release() michael@0: { michael@0: NS_PRECONDITION(0 != mRefCnt, "dup release"); michael@0: nsrefcnt count = --mRefCnt; michael@0: NS_LOG_RELEASE(this, count, "CacheFileChunk"); michael@0: michael@0: if (0 == count) { michael@0: mRefCnt = 1; michael@0: delete (this); michael@0: return 0; michael@0: } michael@0: michael@0: if (!mRemovingChunk && count == 1) { michael@0: mFile->RemoveChunk(this); michael@0: } michael@0: michael@0: return count; michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(CacheFileChunk) michael@0: NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END_THREADSAFE michael@0: michael@0: CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex) michael@0: : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT) michael@0: , mIndex(aIndex) michael@0: , mState(INITIAL) michael@0: , mStatus(NS_OK) michael@0: , mIsDirty(false) michael@0: , mRemovingChunk(false) michael@0: , mDataSize(0) michael@0: , mBuf(nullptr) michael@0: , mBufSize(0) michael@0: , mRWBuf(nullptr) michael@0: , mRWBufSize(0) michael@0: , mReadHash(0) michael@0: , mFile(aFile) michael@0: { michael@0: LOG(("CacheFileChunk::CacheFileChunk() [this=%p]", this)); michael@0: MOZ_COUNT_CTOR(CacheFileChunk); michael@0: } michael@0: michael@0: CacheFileChunk::~CacheFileChunk() michael@0: { michael@0: LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this)); michael@0: MOZ_COUNT_DTOR(CacheFileChunk); michael@0: michael@0: if (mBuf) { michael@0: free(mBuf); michael@0: mBuf = nullptr; michael@0: mBufSize = 0; michael@0: } michael@0: michael@0: if (mRWBuf) { michael@0: free(mRWBuf); michael@0: mRWBuf = nullptr; michael@0: mRWBufSize = 0; michael@0: } michael@0: } michael@0: michael@0: void michael@0: CacheFileChunk::InitNew(CacheFileChunkListener *aCallback) michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback)); michael@0: michael@0: MOZ_ASSERT(mState == INITIAL); michael@0: MOZ_ASSERT(!mBuf); michael@0: MOZ_ASSERT(!mRWBuf); michael@0: michael@0: mBuf = static_cast(moz_xmalloc(kMinBufSize)); michael@0: mBufSize = kMinBufSize; michael@0: mDataSize = 0; michael@0: mState = READY; michael@0: mIsDirty = true; michael@0: michael@0: DoMemoryReport(MemorySize()); michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen, michael@0: CacheHash::Hash16_t aHash, michael@0: CacheFileChunkListener *aCallback) michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]", michael@0: this, aHandle, aLen, aCallback)); michael@0: michael@0: MOZ_ASSERT(mState == INITIAL); michael@0: MOZ_ASSERT(!mBuf); michael@0: MOZ_ASSERT(!mRWBuf); michael@0: MOZ_ASSERT(aLen); michael@0: michael@0: nsresult rv; michael@0: michael@0: mRWBuf = static_cast(moz_xmalloc(aLen)); michael@0: mRWBufSize = aLen; michael@0: michael@0: DoMemoryReport(MemorySize()); michael@0: michael@0: rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize, mRWBuf, aLen, michael@0: true, this); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND; michael@0: SetError(rv); michael@0: } else { michael@0: mState = READING; michael@0: mListener = aCallback; michael@0: mDataSize = aLen; michael@0: mReadHash = aHash; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::Write(CacheFileHandle *aHandle, michael@0: CacheFileChunkListener *aCallback) michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]", michael@0: this, aHandle, aCallback)); michael@0: michael@0: MOZ_ASSERT(mState == READY); michael@0: MOZ_ASSERT(!mRWBuf); michael@0: MOZ_ASSERT(mBuf); michael@0: MOZ_ASSERT(mDataSize); // Don't write chunk when it is empty michael@0: michael@0: nsresult rv; michael@0: michael@0: mRWBuf = mBuf; michael@0: mRWBufSize = mBufSize; michael@0: mBuf = nullptr; michael@0: mBufSize = 0; michael@0: michael@0: rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize, mRWBuf, michael@0: mDataSize, false, this); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: SetError(rv); michael@0: } else { michael@0: mState = WRITING; michael@0: mListener = aCallback; michael@0: mIsDirty = false; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback) michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]", michael@0: this, aCallback)); michael@0: michael@0: MOZ_ASSERT(mFile->mOutput); michael@0: MOZ_ASSERT(IsReady()); michael@0: michael@0: #ifdef DEBUG michael@0: for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) { michael@0: MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback); michael@0: } michael@0: #endif michael@0: michael@0: ChunkListenerItem *item = new ChunkListenerItem(); michael@0: item->mTarget = NS_GetCurrentThread(); michael@0: item->mCallback = aCallback; michael@0: michael@0: mUpdateListeners.AppendElement(item); michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback) michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback)); michael@0: michael@0: MOZ_ASSERT(IsReady()); michael@0: michael@0: uint32_t i; michael@0: for (i = 0 ; i < mUpdateListeners.Length() ; i++) { michael@0: ChunkListenerItem *item = mUpdateListeners[i]; michael@0: michael@0: if (item->mCallback == aCallback) { michael@0: mUpdateListeners.RemoveElementAt(i); michael@0: delete item; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: for ( ; i < mUpdateListeners.Length() ; i++) { michael@0: MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::NotifyUpdateListeners() michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this)); michael@0: michael@0: MOZ_ASSERT(IsReady()); michael@0: michael@0: nsresult rv, rv2; michael@0: michael@0: rv = NS_OK; michael@0: for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) { michael@0: ChunkListenerItem *item = mUpdateListeners[i]; michael@0: michael@0: LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p " michael@0: "[this=%p]", item->mCallback.get(), this)); michael@0: michael@0: nsRefPtr ev; michael@0: ev = new NotifyUpdateListenerEvent(item->mCallback, this); michael@0: rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL); michael@0: if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) michael@0: rv = rv2; michael@0: delete item; michael@0: } michael@0: michael@0: mUpdateListeners.Clear(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t michael@0: CacheFileChunk::Index() michael@0: { michael@0: return mIndex; michael@0: } michael@0: michael@0: CacheHash::Hash16_t michael@0: CacheFileChunk::Hash() michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: MOZ_ASSERT(mBuf); michael@0: MOZ_ASSERT(!mListener); michael@0: MOZ_ASSERT(IsReady()); michael@0: michael@0: return CacheHash::Hash16(BufForReading(), mDataSize); michael@0: } michael@0: michael@0: uint32_t michael@0: CacheFileChunk::DataSize() michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: return mDataSize; michael@0: } michael@0: michael@0: void michael@0: CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF) michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?"); michael@0: MOZ_ASSERT(aOffset <= mDataSize); michael@0: michael@0: // UpdateDataSize() is called only when we've written some data to the chunk michael@0: // and we never write data anymore once some error occurs. michael@0: MOZ_ASSERT(mState != ERROR); michael@0: michael@0: LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]", michael@0: this, aOffset, aLen, aEOF)); michael@0: michael@0: mIsDirty = true; michael@0: michael@0: int64_t fileSize = kChunkSize * mIndex + aOffset + aLen; michael@0: bool notify = false; michael@0: michael@0: if (fileSize > mFile->mDataSize) michael@0: mFile->mDataSize = fileSize; michael@0: michael@0: if (aOffset + aLen > mDataSize) { michael@0: mDataSize = aOffset + aLen; michael@0: notify = true; michael@0: } michael@0: michael@0: if (mState == READY || mState == WRITING) { michael@0: MOZ_ASSERT(mValidityMap.Length() == 0); michael@0: michael@0: if (notify) michael@0: NotifyUpdateListeners(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: // We're still waiting for data from the disk. This chunk cannot be used by michael@0: // input stream, so there must be no update listener. We also need to keep michael@0: // track of where the data is written so that we can correctly merge the new michael@0: // data with the old one. michael@0: michael@0: MOZ_ASSERT(mUpdateListeners.Length() == 0); michael@0: MOZ_ASSERT(mState == READING); michael@0: michael@0: ValidityPair pair(aOffset, aLen); michael@0: michael@0: if (mValidityMap.Length() == 0) { michael@0: mValidityMap.AppendElement(pair); michael@0: return; michael@0: } michael@0: michael@0: michael@0: // Find out where to place this pair into the map, it can overlap with michael@0: // one preceding pair and all subsequent pairs. michael@0: uint32_t pos = 0; michael@0: for (pos = mValidityMap.Length() ; pos > 0 ; pos--) { michael@0: if (mValidityMap[pos-1].LessThan(pair)) { michael@0: if (mValidityMap[pos-1].Overlaps(pair)) { michael@0: // Merge with the preceding pair michael@0: mValidityMap[pos-1].Merge(pair); michael@0: pos--; // Point to the updated pair michael@0: } michael@0: else { michael@0: if (pos == mValidityMap.Length()) michael@0: mValidityMap.AppendElement(pair); michael@0: else michael@0: mValidityMap.InsertElementAt(pos, pair); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!pos) michael@0: mValidityMap.InsertElementAt(0, pair); michael@0: michael@0: // Now pos points to merged or inserted pair, check whether it overlaps with michael@0: // subsequent pairs. michael@0: while (pos + 1 < mValidityMap.Length()) { michael@0: if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) { michael@0: mValidityMap[pos].Merge(mValidityMap[pos + 1]); michael@0: mValidityMap.RemoveElementAt(pos + 1); michael@0: } michael@0: else { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) michael@0: { michael@0: MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf, michael@0: nsresult aResult) michael@0: { michael@0: LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]", michael@0: this, aHandle, aResult)); michael@0: michael@0: nsCOMPtr listener; michael@0: michael@0: { michael@0: CacheFileAutoLock lock(mFile); michael@0: michael@0: MOZ_ASSERT(mState == WRITING); michael@0: MOZ_ASSERT(mListener); michael@0: michael@0: if (NS_WARN_IF(NS_FAILED(aResult))) { michael@0: SetError(aResult); michael@0: } else { michael@0: mState = READY; michael@0: } michael@0: michael@0: if (!mBuf) { michael@0: mBuf = mRWBuf; michael@0: mBufSize = mRWBufSize; michael@0: } else { michael@0: free(mRWBuf); michael@0: } michael@0: michael@0: mRWBuf = nullptr; michael@0: mRWBufSize = 0; michael@0: michael@0: DoMemoryReport(MemorySize()); michael@0: michael@0: mListener.swap(listener); michael@0: } michael@0: michael@0: listener->OnChunkWritten(aResult, this); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf, michael@0: nsresult aResult) michael@0: { michael@0: LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]", michael@0: this, aHandle, aResult)); michael@0: michael@0: nsCOMPtr listener; michael@0: michael@0: { michael@0: CacheFileAutoLock lock(mFile); michael@0: michael@0: MOZ_ASSERT(mState == READING); michael@0: MOZ_ASSERT(mListener); michael@0: michael@0: if (NS_SUCCEEDED(aResult)) { michael@0: CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize); michael@0: if (hash != mReadHash) { michael@0: LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is" michael@0: " %hx, hash in metadata is %hx. [this=%p, idx=%d]", michael@0: hash, mReadHash, this, mIndex)); michael@0: aResult = NS_ERROR_FILE_CORRUPTED; michael@0: } michael@0: else { michael@0: if (!mBuf) { michael@0: // Just swap the buffers if we don't have mBuf yet michael@0: MOZ_ASSERT(mDataSize == mRWBufSize); michael@0: mBuf = mRWBuf; michael@0: mBufSize = mRWBufSize; michael@0: mRWBuf = nullptr; michael@0: mRWBufSize = 0; michael@0: } else { michael@0: // Merge data with write buffer michael@0: if (mRWBufSize < mBufSize) { michael@0: mRWBuf = static_cast(moz_xrealloc(mRWBuf, mBufSize)); michael@0: mRWBufSize = mBufSize; michael@0: } michael@0: michael@0: for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) { michael@0: memcpy(mRWBuf + mValidityMap[i].Offset(), michael@0: mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len()); michael@0: } michael@0: michael@0: free(mBuf); michael@0: mBuf = mRWBuf; michael@0: mBufSize = mRWBufSize; michael@0: mRWBuf = nullptr; michael@0: mRWBufSize = 0; michael@0: michael@0: DoMemoryReport(MemorySize()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(aResult)) { michael@0: aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND; michael@0: SetError(aResult); michael@0: mDataSize = 0; michael@0: } else { michael@0: mState = READY; michael@0: } michael@0: michael@0: mListener.swap(listener); michael@0: } michael@0: michael@0: listener->OnChunkRead(aResult, this); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) michael@0: { michael@0: MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) michael@0: { michael@0: MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) michael@0: { michael@0: MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: bool michael@0: CacheFileChunk::IsReady() const michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING)); michael@0: } michael@0: michael@0: bool michael@0: CacheFileChunk::IsDirty() const michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: return mIsDirty; michael@0: } michael@0: michael@0: nsresult michael@0: CacheFileChunk::GetStatus() michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: return mStatus; michael@0: } michael@0: michael@0: void michael@0: CacheFileChunk::SetError(nsresult aStatus) michael@0: { michael@0: if (NS_SUCCEEDED(mStatus)) { michael@0: MOZ_ASSERT(mState != ERROR); michael@0: mStatus = aStatus; michael@0: mState = ERROR; michael@0: } else { michael@0: MOZ_ASSERT(mState == ERROR); michael@0: } michael@0: } michael@0: michael@0: char * michael@0: CacheFileChunk::BufForWriting() const michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize() michael@0: michael@0: MOZ_ASSERT((mState == READY && !mRWBuf) || michael@0: (mState == WRITING && mRWBuf) || michael@0: (mState == READING && mRWBuf)); michael@0: michael@0: return mBuf; michael@0: } michael@0: michael@0: const char * michael@0: CacheFileChunk::BufForReading() const michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) || michael@0: (mState == WRITING && mRWBuf)); michael@0: michael@0: return mBuf ? mBuf : mRWBuf; michael@0: } michael@0: michael@0: void michael@0: CacheFileChunk::EnsureBufSize(uint32_t aBufSize) michael@0: { michael@0: mFile->AssertOwnsLock(); michael@0: michael@0: // EnsureBufSize() is called only when we want to write some data to the chunk michael@0: // and we never write data anymore once some error occurs. michael@0: MOZ_ASSERT(mState != ERROR); michael@0: michael@0: if (mBufSize >= aBufSize) michael@0: return; michael@0: michael@0: bool copy = false; michael@0: if (!mBuf && mState == WRITING) { michael@0: // We need to duplicate the data that is being written on the background michael@0: // thread, so make sure that all the data fits into the new buffer. michael@0: copy = true; michael@0: michael@0: if (mRWBufSize > aBufSize) michael@0: aBufSize = mRWBufSize; michael@0: } michael@0: michael@0: // find smallest power of 2 greater than or equal to aBufSize michael@0: aBufSize--; michael@0: aBufSize |= aBufSize >> 1; michael@0: aBufSize |= aBufSize >> 2; michael@0: aBufSize |= aBufSize >> 4; michael@0: aBufSize |= aBufSize >> 8; michael@0: aBufSize |= aBufSize >> 16; michael@0: aBufSize++; michael@0: michael@0: const uint32_t minBufSize = kMinBufSize; michael@0: const uint32_t maxBufSize = kChunkSize; michael@0: aBufSize = clamped(aBufSize, minBufSize, maxBufSize); michael@0: michael@0: mBuf = static_cast(moz_xrealloc(mBuf, aBufSize)); michael@0: mBufSize = aBufSize; michael@0: michael@0: if (copy) michael@0: memcpy(mBuf, mRWBuf, mRWBufSize); michael@0: michael@0: DoMemoryReport(MemorySize()); michael@0: } michael@0: michael@0: // Memory reporting michael@0: michael@0: size_t michael@0: CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: size_t n = 0; michael@0: n += mallocSizeOf(mBuf); michael@0: n += mallocSizeOf(mRWBuf); michael@0: n += mValidityMap.SizeOfExcludingThis(mallocSizeOf); michael@0: michael@0: return n; michael@0: } michael@0: michael@0: size_t michael@0: CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); michael@0: } michael@0: michael@0: } // net michael@0: } // mozilla