netwerk/cache2/CacheFileChunk.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/cache2/CacheFileChunk.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,717 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "CacheLog.h"
     1.9 +#include "CacheFileChunk.h"
    1.10 +
    1.11 +#include "CacheFile.h"
    1.12 +#include "nsThreadUtils.h"
    1.13 +#include "nsAlgorithm.h"
    1.14 +#include <algorithm>
    1.15 +
    1.16 +namespace mozilla {
    1.17 +namespace net {
    1.18 +
    1.19 +#define kMinBufSize        512
    1.20 +
    1.21 +class NotifyUpdateListenerEvent : public nsRunnable {
    1.22 +public:
    1.23 +  NotifyUpdateListenerEvent(CacheFileChunkListener *aCallback,
    1.24 +                            CacheFileChunk *aChunk)
    1.25 +    : mCallback(aCallback)
    1.26 +    , mChunk(aChunk)
    1.27 +  {
    1.28 +    LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
    1.29 +         this));
    1.30 +    MOZ_COUNT_CTOR(NotifyUpdateListenerEvent);
    1.31 +  }
    1.32 +
    1.33 +  ~NotifyUpdateListenerEvent()
    1.34 +  {
    1.35 +    LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
    1.36 +         this));
    1.37 +    MOZ_COUNT_DTOR(NotifyUpdateListenerEvent);
    1.38 +  }
    1.39 +
    1.40 +  NS_IMETHOD Run()
    1.41 +  {
    1.42 +    LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
    1.43 +
    1.44 +    mCallback->OnChunkUpdated(mChunk);
    1.45 +    return NS_OK;
    1.46 +  }
    1.47 +
    1.48 +protected:
    1.49 +  nsCOMPtr<CacheFileChunkListener> mCallback;
    1.50 +  nsRefPtr<CacheFileChunk>         mChunk;
    1.51 +};
    1.52 +
    1.53 +
    1.54 +class ValidityPair {
    1.55 +public:
    1.56 +  ValidityPair(uint32_t aOffset, uint32_t aLen)
    1.57 +    : mOffset(aOffset), mLen(aLen)
    1.58 +  {}
    1.59 +
    1.60 +  ValidityPair& operator=(const ValidityPair& aOther) {
    1.61 +    mOffset = aOther.mOffset;
    1.62 +    mLen = aOther.mLen;
    1.63 +    return *this;
    1.64 +  }
    1.65 +
    1.66 +  bool Overlaps(const ValidityPair& aOther) const {
    1.67 +    if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) ||
    1.68 +        (aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset))
    1.69 +      return true;
    1.70 +
    1.71 +    return false;
    1.72 +  }
    1.73 +
    1.74 +  bool LessThan(const ValidityPair& aOther) const {
    1.75 +    if (mOffset < aOther.mOffset)
    1.76 +      return true;
    1.77 +
    1.78 +    if (mOffset == aOther.mOffset && mLen < aOther.mLen)
    1.79 +      return true;
    1.80 +
    1.81 +    return false;
    1.82 +  }
    1.83 +
    1.84 +  void Merge(const ValidityPair& aOther) {
    1.85 +    MOZ_ASSERT(Overlaps(aOther));
    1.86 +
    1.87 +    uint32_t offset = std::min(mOffset, aOther.mOffset);
    1.88 +    uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
    1.89 +
    1.90 +    mOffset = offset;
    1.91 +    mLen = end - offset;
    1.92 +  }
    1.93 +
    1.94 +  uint32_t Offset() { return mOffset; }
    1.95 +  uint32_t Len()    { return mLen; }
    1.96 +
    1.97 +private:
    1.98 +  uint32_t mOffset;
    1.99 +  uint32_t mLen;
   1.100 +};
   1.101 +
   1.102 +
   1.103 +NS_IMPL_ADDREF(CacheFileChunk)
   1.104 +NS_IMETHODIMP_(MozExternalRefCountType)
   1.105 +CacheFileChunk::Release()
   1.106 +{
   1.107 +  NS_PRECONDITION(0 != mRefCnt, "dup release");
   1.108 +  nsrefcnt count = --mRefCnt;
   1.109 +  NS_LOG_RELEASE(this, count, "CacheFileChunk");
   1.110 +
   1.111 +  if (0 == count) {
   1.112 +    mRefCnt = 1;
   1.113 +    delete (this);
   1.114 +    return 0;
   1.115 +  }
   1.116 +
   1.117 +  if (!mRemovingChunk && count == 1) {
   1.118 +    mFile->RemoveChunk(this);
   1.119 +  }
   1.120 +
   1.121 +  return count;
   1.122 +}
   1.123 +
   1.124 +NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
   1.125 +  NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
   1.126 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.127 +NS_INTERFACE_MAP_END_THREADSAFE
   1.128 +
   1.129 +CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex)
   1.130 +  : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT)
   1.131 +  , mIndex(aIndex)
   1.132 +  , mState(INITIAL)
   1.133 +  , mStatus(NS_OK)
   1.134 +  , mIsDirty(false)
   1.135 +  , mRemovingChunk(false)
   1.136 +  , mDataSize(0)
   1.137 +  , mBuf(nullptr)
   1.138 +  , mBufSize(0)
   1.139 +  , mRWBuf(nullptr)
   1.140 +  , mRWBufSize(0)
   1.141 +  , mReadHash(0)
   1.142 +  , mFile(aFile)
   1.143 +{
   1.144 +  LOG(("CacheFileChunk::CacheFileChunk() [this=%p]", this));
   1.145 +  MOZ_COUNT_CTOR(CacheFileChunk);
   1.146 +}
   1.147 +
   1.148 +CacheFileChunk::~CacheFileChunk()
   1.149 +{
   1.150 +  LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
   1.151 +  MOZ_COUNT_DTOR(CacheFileChunk);
   1.152 +
   1.153 +  if (mBuf) {
   1.154 +    free(mBuf);
   1.155 +    mBuf = nullptr;
   1.156 +    mBufSize = 0;
   1.157 +  }
   1.158 +
   1.159 +  if (mRWBuf) {
   1.160 +    free(mRWBuf);
   1.161 +    mRWBuf = nullptr;
   1.162 +    mRWBufSize = 0;
   1.163 +  }
   1.164 +}
   1.165 +
   1.166 +void
   1.167 +CacheFileChunk::InitNew(CacheFileChunkListener *aCallback)
   1.168 +{
   1.169 +  mFile->AssertOwnsLock();
   1.170 +
   1.171 +  LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback));
   1.172 +
   1.173 +  MOZ_ASSERT(mState == INITIAL);
   1.174 +  MOZ_ASSERT(!mBuf);
   1.175 +  MOZ_ASSERT(!mRWBuf);
   1.176 +
   1.177 +  mBuf = static_cast<char *>(moz_xmalloc(kMinBufSize));
   1.178 +  mBufSize = kMinBufSize;
   1.179 +  mDataSize = 0;
   1.180 +  mState = READY;
   1.181 +  mIsDirty = true;
   1.182 +
   1.183 +  DoMemoryReport(MemorySize());
   1.184 +}
   1.185 +
   1.186 +nsresult
   1.187 +CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
   1.188 +                     CacheHash::Hash16_t aHash,
   1.189 +                     CacheFileChunkListener *aCallback)
   1.190 +{
   1.191 +  mFile->AssertOwnsLock();
   1.192 +
   1.193 +  LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
   1.194 +       this, aHandle, aLen, aCallback));
   1.195 +
   1.196 +  MOZ_ASSERT(mState == INITIAL);
   1.197 +  MOZ_ASSERT(!mBuf);
   1.198 +  MOZ_ASSERT(!mRWBuf);
   1.199 +  MOZ_ASSERT(aLen);
   1.200 +
   1.201 +  nsresult rv;
   1.202 +
   1.203 +  mRWBuf = static_cast<char *>(moz_xmalloc(aLen));
   1.204 +  mRWBufSize = aLen;
   1.205 +
   1.206 +  DoMemoryReport(MemorySize());
   1.207 +
   1.208 +  rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize, mRWBuf, aLen,
   1.209 +                                true, this);
   1.210 +  if (NS_WARN_IF(NS_FAILED(rv))) {
   1.211 +    rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
   1.212 +    SetError(rv);
   1.213 +  } else {
   1.214 +    mState = READING;
   1.215 +    mListener = aCallback;
   1.216 +    mDataSize = aLen;
   1.217 +    mReadHash = aHash;
   1.218 +  }
   1.219 +
   1.220 +  return rv;
   1.221 +}
   1.222 +
   1.223 +nsresult
   1.224 +CacheFileChunk::Write(CacheFileHandle *aHandle,
   1.225 +                      CacheFileChunkListener *aCallback)
   1.226 +{
   1.227 +  mFile->AssertOwnsLock();
   1.228 +
   1.229 +  LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
   1.230 +       this, aHandle, aCallback));
   1.231 +
   1.232 +  MOZ_ASSERT(mState == READY);
   1.233 +  MOZ_ASSERT(!mRWBuf);
   1.234 +  MOZ_ASSERT(mBuf);
   1.235 +  MOZ_ASSERT(mDataSize); // Don't write chunk when it is empty
   1.236 +
   1.237 +  nsresult rv;
   1.238 +
   1.239 +  mRWBuf = mBuf;
   1.240 +  mRWBufSize = mBufSize;
   1.241 +  mBuf = nullptr;
   1.242 +  mBufSize = 0;
   1.243 +
   1.244 +  rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize, mRWBuf,
   1.245 +                                 mDataSize, false, this);
   1.246 +  if (NS_WARN_IF(NS_FAILED(rv))) {
   1.247 +    SetError(rv);
   1.248 +  } else {
   1.249 +    mState = WRITING;
   1.250 +    mListener = aCallback;
   1.251 +    mIsDirty = false;
   1.252 +  }
   1.253 +
   1.254 +  return rv;
   1.255 +}
   1.256 +
   1.257 +void
   1.258 +CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
   1.259 +{
   1.260 +  mFile->AssertOwnsLock();
   1.261 +
   1.262 +  LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
   1.263 +       this, aCallback));
   1.264 +
   1.265 +  MOZ_ASSERT(mFile->mOutput);
   1.266 +  MOZ_ASSERT(IsReady());
   1.267 +
   1.268 +#ifdef DEBUG
   1.269 +  for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
   1.270 +    MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
   1.271 +  }
   1.272 +#endif
   1.273 +
   1.274 +  ChunkListenerItem *item = new ChunkListenerItem();
   1.275 +  item->mTarget = NS_GetCurrentThread();
   1.276 +  item->mCallback = aCallback;
   1.277 +
   1.278 +  mUpdateListeners.AppendElement(item);
   1.279 +}
   1.280 +
   1.281 +nsresult
   1.282 +CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
   1.283 +{
   1.284 +  mFile->AssertOwnsLock();
   1.285 +
   1.286 +  LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
   1.287 +
   1.288 +  MOZ_ASSERT(IsReady());
   1.289 +
   1.290 +  uint32_t i;
   1.291 +  for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
   1.292 +    ChunkListenerItem *item = mUpdateListeners[i];
   1.293 +
   1.294 +    if (item->mCallback == aCallback) {
   1.295 +      mUpdateListeners.RemoveElementAt(i);
   1.296 +      delete item;
   1.297 +      break;
   1.298 +    }
   1.299 +  }
   1.300 +
   1.301 +#ifdef DEBUG
   1.302 +  for ( ; i < mUpdateListeners.Length() ; i++) {
   1.303 +    MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
   1.304 +  }
   1.305 +#endif
   1.306 +
   1.307 +  return NS_OK;
   1.308 +}
   1.309 +
   1.310 +nsresult
   1.311 +CacheFileChunk::NotifyUpdateListeners()
   1.312 +{
   1.313 +  mFile->AssertOwnsLock();
   1.314 +
   1.315 +  LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
   1.316 +
   1.317 +  MOZ_ASSERT(IsReady());
   1.318 +
   1.319 +  nsresult rv, rv2;
   1.320 +
   1.321 +  rv = NS_OK;
   1.322 +  for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
   1.323 +    ChunkListenerItem *item = mUpdateListeners[i];
   1.324 +
   1.325 +    LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
   1.326 +         "[this=%p]", item->mCallback.get(), this));
   1.327 +
   1.328 +    nsRefPtr<NotifyUpdateListenerEvent> ev;
   1.329 +    ev = new NotifyUpdateListenerEvent(item->mCallback, this);
   1.330 +    rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
   1.331 +    if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
   1.332 +      rv = rv2;
   1.333 +    delete item;
   1.334 +  }
   1.335 +
   1.336 +  mUpdateListeners.Clear();
   1.337 +
   1.338 +  return rv;
   1.339 +}
   1.340 +
   1.341 +uint32_t
   1.342 +CacheFileChunk::Index()
   1.343 +{
   1.344 +  return mIndex;
   1.345 +}
   1.346 +
   1.347 +CacheHash::Hash16_t
   1.348 +CacheFileChunk::Hash()
   1.349 +{
   1.350 +  mFile->AssertOwnsLock();
   1.351 +
   1.352 +  MOZ_ASSERT(mBuf);
   1.353 +  MOZ_ASSERT(!mListener);
   1.354 +  MOZ_ASSERT(IsReady());
   1.355 +
   1.356 +  return CacheHash::Hash16(BufForReading(), mDataSize);
   1.357 +}
   1.358 +
   1.359 +uint32_t
   1.360 +CacheFileChunk::DataSize()
   1.361 +{
   1.362 +  mFile->AssertOwnsLock();
   1.363 +  return mDataSize;
   1.364 +}
   1.365 +
   1.366 +void
   1.367 +CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF)
   1.368 +{
   1.369 +  mFile->AssertOwnsLock();
   1.370 +
   1.371 +  MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?");
   1.372 +  MOZ_ASSERT(aOffset <= mDataSize);
   1.373 +
   1.374 +  // UpdateDataSize() is called only when we've written some data to the chunk
   1.375 +  // and we never write data anymore once some error occurs.
   1.376 +  MOZ_ASSERT(mState != ERROR);
   1.377 +
   1.378 +  LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]",
   1.379 +       this, aOffset, aLen, aEOF));
   1.380 +
   1.381 +  mIsDirty = true;
   1.382 +
   1.383 +  int64_t fileSize = kChunkSize * mIndex + aOffset + aLen;
   1.384 +  bool notify = false;
   1.385 +
   1.386 +  if (fileSize > mFile->mDataSize)
   1.387 +    mFile->mDataSize = fileSize;
   1.388 +
   1.389 +  if (aOffset + aLen > mDataSize) {
   1.390 +    mDataSize = aOffset + aLen;
   1.391 +    notify = true;
   1.392 +  }
   1.393 +
   1.394 +  if (mState == READY || mState == WRITING) {
   1.395 +    MOZ_ASSERT(mValidityMap.Length() == 0);
   1.396 +
   1.397 +    if (notify)
   1.398 +      NotifyUpdateListeners();
   1.399 +
   1.400 +    return;
   1.401 +  }
   1.402 +
   1.403 +  // We're still waiting for data from the disk. This chunk cannot be used by
   1.404 +  // input stream, so there must be no update listener. We also need to keep
   1.405 +  // track of where the data is written so that we can correctly merge the new
   1.406 +  // data with the old one.
   1.407 +
   1.408 +  MOZ_ASSERT(mUpdateListeners.Length() == 0);
   1.409 +  MOZ_ASSERT(mState == READING);
   1.410 +
   1.411 +  ValidityPair pair(aOffset, aLen);
   1.412 +
   1.413 +  if (mValidityMap.Length() == 0) {
   1.414 +    mValidityMap.AppendElement(pair);
   1.415 +    return;
   1.416 +  }
   1.417 +
   1.418 +
   1.419 +  // Find out where to place this pair into the map, it can overlap with
   1.420 +  // one preceding pair and all subsequent pairs.
   1.421 +  uint32_t pos = 0;
   1.422 +  for (pos = mValidityMap.Length() ; pos > 0 ; pos--) {
   1.423 +    if (mValidityMap[pos-1].LessThan(pair)) {
   1.424 +      if (mValidityMap[pos-1].Overlaps(pair)) {
   1.425 +        // Merge with the preceding pair
   1.426 +        mValidityMap[pos-1].Merge(pair);
   1.427 +        pos--; // Point to the updated pair
   1.428 +      }
   1.429 +      else {
   1.430 +        if (pos == mValidityMap.Length())
   1.431 +          mValidityMap.AppendElement(pair);
   1.432 +        else
   1.433 +          mValidityMap.InsertElementAt(pos, pair);
   1.434 +      }
   1.435 +
   1.436 +      break;
   1.437 +    }
   1.438 +  }
   1.439 +
   1.440 +  if (!pos)
   1.441 +    mValidityMap.InsertElementAt(0, pair);
   1.442 +
   1.443 +  // Now pos points to merged or inserted pair, check whether it overlaps with
   1.444 +  // subsequent pairs.
   1.445 +  while (pos + 1 < mValidityMap.Length()) {
   1.446 +    if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) {
   1.447 +      mValidityMap[pos].Merge(mValidityMap[pos + 1]);
   1.448 +      mValidityMap.RemoveElementAt(pos + 1);
   1.449 +    }
   1.450 +    else {
   1.451 +      break;
   1.452 +    }
   1.453 +  }
   1.454 +}
   1.455 +
   1.456 +nsresult
   1.457 +CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
   1.458 +{
   1.459 +  MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
   1.460 +  return NS_ERROR_UNEXPECTED;
   1.461 +}
   1.462 +
   1.463 +nsresult
   1.464 +CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
   1.465 +                              nsresult aResult)
   1.466 +{
   1.467 +  LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]",
   1.468 +       this, aHandle, aResult));
   1.469 +
   1.470 +  nsCOMPtr<CacheFileChunkListener> listener;
   1.471 +
   1.472 +  {
   1.473 +    CacheFileAutoLock lock(mFile);
   1.474 +
   1.475 +    MOZ_ASSERT(mState == WRITING);
   1.476 +    MOZ_ASSERT(mListener);
   1.477 +
   1.478 +    if (NS_WARN_IF(NS_FAILED(aResult))) {
   1.479 +      SetError(aResult);
   1.480 +    } else {
   1.481 +      mState = READY;
   1.482 +    }
   1.483 +
   1.484 +    if (!mBuf) {
   1.485 +      mBuf = mRWBuf;
   1.486 +      mBufSize = mRWBufSize;
   1.487 +    } else {
   1.488 +      free(mRWBuf);
   1.489 +    }
   1.490 +
   1.491 +    mRWBuf = nullptr;
   1.492 +    mRWBufSize = 0;
   1.493 +
   1.494 +    DoMemoryReport(MemorySize());
   1.495 +
   1.496 +    mListener.swap(listener);
   1.497 +  }
   1.498 +
   1.499 +  listener->OnChunkWritten(aResult, this);
   1.500 +
   1.501 +  return NS_OK;
   1.502 +}
   1.503 +
   1.504 +nsresult
   1.505 +CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
   1.506 +                           nsresult aResult)
   1.507 +{
   1.508 +  LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
   1.509 +       this, aHandle, aResult));
   1.510 +
   1.511 +  nsCOMPtr<CacheFileChunkListener> listener;
   1.512 +
   1.513 +  {
   1.514 +    CacheFileAutoLock lock(mFile);
   1.515 +
   1.516 +    MOZ_ASSERT(mState == READING);
   1.517 +    MOZ_ASSERT(mListener);
   1.518 +
   1.519 +    if (NS_SUCCEEDED(aResult)) {
   1.520 +      CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize);
   1.521 +      if (hash != mReadHash) {
   1.522 +        LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
   1.523 +             " %hx, hash in metadata is %hx. [this=%p, idx=%d]",
   1.524 +             hash, mReadHash, this, mIndex));
   1.525 +        aResult = NS_ERROR_FILE_CORRUPTED;
   1.526 +      }
   1.527 +      else {
   1.528 +        if (!mBuf) {
   1.529 +          // Just swap the buffers if we don't have mBuf yet
   1.530 +          MOZ_ASSERT(mDataSize == mRWBufSize);
   1.531 +          mBuf = mRWBuf;
   1.532 +          mBufSize = mRWBufSize;
   1.533 +          mRWBuf = nullptr;
   1.534 +          mRWBufSize = 0;
   1.535 +        } else {
   1.536 +          // Merge data with write buffer
   1.537 +          if (mRWBufSize < mBufSize) {
   1.538 +            mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize));
   1.539 +            mRWBufSize = mBufSize;
   1.540 +          }
   1.541 +
   1.542 +          for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) {
   1.543 +            memcpy(mRWBuf + mValidityMap[i].Offset(),
   1.544 +                   mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len());
   1.545 +          }
   1.546 +
   1.547 +          free(mBuf);
   1.548 +          mBuf = mRWBuf;
   1.549 +          mBufSize = mRWBufSize;
   1.550 +          mRWBuf = nullptr;
   1.551 +          mRWBufSize = 0;
   1.552 +
   1.553 +          DoMemoryReport(MemorySize());
   1.554 +        }
   1.555 +      }
   1.556 +    }
   1.557 +
   1.558 +    if (NS_FAILED(aResult)) {
   1.559 +      aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
   1.560 +      SetError(aResult);
   1.561 +      mDataSize = 0;
   1.562 +    } else {
   1.563 +      mState = READY;
   1.564 +    }
   1.565 +
   1.566 +    mListener.swap(listener);
   1.567 +  }
   1.568 +
   1.569 +  listener->OnChunkRead(aResult, this);
   1.570 +
   1.571 +  return NS_OK;
   1.572 +}
   1.573 +
   1.574 +nsresult
   1.575 +CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
   1.576 +{
   1.577 +  MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
   1.578 +  return NS_ERROR_UNEXPECTED;
   1.579 +}
   1.580 +
   1.581 +nsresult
   1.582 +CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
   1.583 +{
   1.584 +  MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
   1.585 +  return NS_ERROR_UNEXPECTED;
   1.586 +}
   1.587 +
   1.588 +nsresult
   1.589 +CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
   1.590 +{
   1.591 +  MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
   1.592 +  return NS_ERROR_UNEXPECTED;
   1.593 +}
   1.594 +
   1.595 +bool
   1.596 +CacheFileChunk::IsReady() const
   1.597 +{
   1.598 +  mFile->AssertOwnsLock();
   1.599 +
   1.600 +  return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
   1.601 +}
   1.602 +
   1.603 +bool
   1.604 +CacheFileChunk::IsDirty() const
   1.605 +{
   1.606 +  mFile->AssertOwnsLock();
   1.607 +
   1.608 +  return mIsDirty;
   1.609 +}
   1.610 +
   1.611 +nsresult
   1.612 +CacheFileChunk::GetStatus()
   1.613 +{
   1.614 +  mFile->AssertOwnsLock();
   1.615 +
   1.616 +  return mStatus;
   1.617 +}
   1.618 +
   1.619 +void
   1.620 +CacheFileChunk::SetError(nsresult aStatus)
   1.621 +{
   1.622 +  if (NS_SUCCEEDED(mStatus)) {
   1.623 +    MOZ_ASSERT(mState != ERROR);
   1.624 +    mStatus = aStatus;
   1.625 +    mState = ERROR;
   1.626 +  } else {
   1.627 +    MOZ_ASSERT(mState == ERROR);
   1.628 +  }
   1.629 +}
   1.630 +
   1.631 +char *
   1.632 +CacheFileChunk::BufForWriting() const
   1.633 +{
   1.634 +  mFile->AssertOwnsLock();
   1.635 +
   1.636 +  MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize()
   1.637 +
   1.638 +  MOZ_ASSERT((mState == READY && !mRWBuf) ||
   1.639 +             (mState == WRITING && mRWBuf) ||
   1.640 +             (mState == READING && mRWBuf));
   1.641 +
   1.642 +  return mBuf;
   1.643 +}
   1.644 +
   1.645 +const char *
   1.646 +CacheFileChunk::BufForReading() const
   1.647 +{
   1.648 +  mFile->AssertOwnsLock();
   1.649 +
   1.650 +  MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) ||
   1.651 +             (mState == WRITING && mRWBuf));
   1.652 +
   1.653 +  return mBuf ? mBuf : mRWBuf;
   1.654 +}
   1.655 +
   1.656 +void
   1.657 +CacheFileChunk::EnsureBufSize(uint32_t aBufSize)
   1.658 +{
   1.659 +  mFile->AssertOwnsLock();
   1.660 +
   1.661 +  // EnsureBufSize() is called only when we want to write some data to the chunk
   1.662 +  // and we never write data anymore once some error occurs.
   1.663 +  MOZ_ASSERT(mState != ERROR);
   1.664 +
   1.665 +  if (mBufSize >= aBufSize)
   1.666 +    return;
   1.667 +
   1.668 +  bool copy = false;
   1.669 +  if (!mBuf && mState == WRITING) {
   1.670 +    // We need to duplicate the data that is being written on the background
   1.671 +    // thread, so make sure that all the data fits into the new buffer.
   1.672 +    copy = true;
   1.673 +
   1.674 +    if (mRWBufSize > aBufSize)
   1.675 +      aBufSize = mRWBufSize;
   1.676 +  }
   1.677 +
   1.678 +  // find smallest power of 2 greater than or equal to aBufSize
   1.679 +  aBufSize--;
   1.680 +  aBufSize |= aBufSize >> 1;
   1.681 +  aBufSize |= aBufSize >> 2;
   1.682 +  aBufSize |= aBufSize >> 4;
   1.683 +  aBufSize |= aBufSize >> 8;
   1.684 +  aBufSize |= aBufSize >> 16;
   1.685 +  aBufSize++;
   1.686 +
   1.687 +  const uint32_t minBufSize = kMinBufSize;
   1.688 +  const uint32_t maxBufSize = kChunkSize;
   1.689 +  aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
   1.690 +
   1.691 +  mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize));
   1.692 +  mBufSize = aBufSize;
   1.693 +
   1.694 +  if (copy)
   1.695 +    memcpy(mBuf, mRWBuf, mRWBufSize);
   1.696 +
   1.697 +  DoMemoryReport(MemorySize());
   1.698 +}
   1.699 +
   1.700 +// Memory reporting
   1.701 +
   1.702 +size_t
   1.703 +CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   1.704 +{
   1.705 +  size_t n = 0;
   1.706 +  n += mallocSizeOf(mBuf);
   1.707 +  n += mallocSizeOf(mRWBuf);
   1.708 +  n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
   1.709 +
   1.710 +  return n;
   1.711 +}
   1.712 +
   1.713 +size_t
   1.714 +CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   1.715 +{
   1.716 +  return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
   1.717 +}
   1.718 +
   1.719 +} // net
   1.720 +} // mozilla

mercurial