netwerk/cache2/CacheFileChunk.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "CacheLog.h"
michael@0 6 #include "CacheFileChunk.h"
michael@0 7
michael@0 8 #include "CacheFile.h"
michael@0 9 #include "nsThreadUtils.h"
michael@0 10 #include "nsAlgorithm.h"
michael@0 11 #include <algorithm>
michael@0 12
michael@0 13 namespace mozilla {
michael@0 14 namespace net {
michael@0 15
michael@0 16 #define kMinBufSize 512
michael@0 17
michael@0 18 class NotifyUpdateListenerEvent : public nsRunnable {
michael@0 19 public:
michael@0 20 NotifyUpdateListenerEvent(CacheFileChunkListener *aCallback,
michael@0 21 CacheFileChunk *aChunk)
michael@0 22 : mCallback(aCallback)
michael@0 23 , mChunk(aChunk)
michael@0 24 {
michael@0 25 LOG(("NotifyUpdateListenerEvent::NotifyUpdateListenerEvent() [this=%p]",
michael@0 26 this));
michael@0 27 MOZ_COUNT_CTOR(NotifyUpdateListenerEvent);
michael@0 28 }
michael@0 29
michael@0 30 ~NotifyUpdateListenerEvent()
michael@0 31 {
michael@0 32 LOG(("NotifyUpdateListenerEvent::~NotifyUpdateListenerEvent() [this=%p]",
michael@0 33 this));
michael@0 34 MOZ_COUNT_DTOR(NotifyUpdateListenerEvent);
michael@0 35 }
michael@0 36
michael@0 37 NS_IMETHOD Run()
michael@0 38 {
michael@0 39 LOG(("NotifyUpdateListenerEvent::Run() [this=%p]", this));
michael@0 40
michael@0 41 mCallback->OnChunkUpdated(mChunk);
michael@0 42 return NS_OK;
michael@0 43 }
michael@0 44
michael@0 45 protected:
michael@0 46 nsCOMPtr<CacheFileChunkListener> mCallback;
michael@0 47 nsRefPtr<CacheFileChunk> mChunk;
michael@0 48 };
michael@0 49
michael@0 50
michael@0 51 class ValidityPair {
michael@0 52 public:
michael@0 53 ValidityPair(uint32_t aOffset, uint32_t aLen)
michael@0 54 : mOffset(aOffset), mLen(aLen)
michael@0 55 {}
michael@0 56
michael@0 57 ValidityPair& operator=(const ValidityPair& aOther) {
michael@0 58 mOffset = aOther.mOffset;
michael@0 59 mLen = aOther.mLen;
michael@0 60 return *this;
michael@0 61 }
michael@0 62
michael@0 63 bool Overlaps(const ValidityPair& aOther) const {
michael@0 64 if ((mOffset <= aOther.mOffset && mOffset + mLen >= aOther.mOffset) ||
michael@0 65 (aOther.mOffset <= mOffset && aOther.mOffset + mLen >= mOffset))
michael@0 66 return true;
michael@0 67
michael@0 68 return false;
michael@0 69 }
michael@0 70
michael@0 71 bool LessThan(const ValidityPair& aOther) const {
michael@0 72 if (mOffset < aOther.mOffset)
michael@0 73 return true;
michael@0 74
michael@0 75 if (mOffset == aOther.mOffset && mLen < aOther.mLen)
michael@0 76 return true;
michael@0 77
michael@0 78 return false;
michael@0 79 }
michael@0 80
michael@0 81 void Merge(const ValidityPair& aOther) {
michael@0 82 MOZ_ASSERT(Overlaps(aOther));
michael@0 83
michael@0 84 uint32_t offset = std::min(mOffset, aOther.mOffset);
michael@0 85 uint32_t end = std::max(mOffset + mLen, aOther.mOffset + aOther.mLen);
michael@0 86
michael@0 87 mOffset = offset;
michael@0 88 mLen = end - offset;
michael@0 89 }
michael@0 90
michael@0 91 uint32_t Offset() { return mOffset; }
michael@0 92 uint32_t Len() { return mLen; }
michael@0 93
michael@0 94 private:
michael@0 95 uint32_t mOffset;
michael@0 96 uint32_t mLen;
michael@0 97 };
michael@0 98
michael@0 99
michael@0 100 NS_IMPL_ADDREF(CacheFileChunk)
michael@0 101 NS_IMETHODIMP_(MozExternalRefCountType)
michael@0 102 CacheFileChunk::Release()
michael@0 103 {
michael@0 104 NS_PRECONDITION(0 != mRefCnt, "dup release");
michael@0 105 nsrefcnt count = --mRefCnt;
michael@0 106 NS_LOG_RELEASE(this, count, "CacheFileChunk");
michael@0 107
michael@0 108 if (0 == count) {
michael@0 109 mRefCnt = 1;
michael@0 110 delete (this);
michael@0 111 return 0;
michael@0 112 }
michael@0 113
michael@0 114 if (!mRemovingChunk && count == 1) {
michael@0 115 mFile->RemoveChunk(this);
michael@0 116 }
michael@0 117
michael@0 118 return count;
michael@0 119 }
michael@0 120
michael@0 121 NS_INTERFACE_MAP_BEGIN(CacheFileChunk)
michael@0 122 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
michael@0 123 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 124 NS_INTERFACE_MAP_END_THREADSAFE
michael@0 125
michael@0 126 CacheFileChunk::CacheFileChunk(CacheFile *aFile, uint32_t aIndex)
michael@0 127 : CacheMemoryConsumer(aFile->mOpenAsMemoryOnly ? MEMORY_ONLY : DONT_REPORT)
michael@0 128 , mIndex(aIndex)
michael@0 129 , mState(INITIAL)
michael@0 130 , mStatus(NS_OK)
michael@0 131 , mIsDirty(false)
michael@0 132 , mRemovingChunk(false)
michael@0 133 , mDataSize(0)
michael@0 134 , mBuf(nullptr)
michael@0 135 , mBufSize(0)
michael@0 136 , mRWBuf(nullptr)
michael@0 137 , mRWBufSize(0)
michael@0 138 , mReadHash(0)
michael@0 139 , mFile(aFile)
michael@0 140 {
michael@0 141 LOG(("CacheFileChunk::CacheFileChunk() [this=%p]", this));
michael@0 142 MOZ_COUNT_CTOR(CacheFileChunk);
michael@0 143 }
michael@0 144
michael@0 145 CacheFileChunk::~CacheFileChunk()
michael@0 146 {
michael@0 147 LOG(("CacheFileChunk::~CacheFileChunk() [this=%p]", this));
michael@0 148 MOZ_COUNT_DTOR(CacheFileChunk);
michael@0 149
michael@0 150 if (mBuf) {
michael@0 151 free(mBuf);
michael@0 152 mBuf = nullptr;
michael@0 153 mBufSize = 0;
michael@0 154 }
michael@0 155
michael@0 156 if (mRWBuf) {
michael@0 157 free(mRWBuf);
michael@0 158 mRWBuf = nullptr;
michael@0 159 mRWBufSize = 0;
michael@0 160 }
michael@0 161 }
michael@0 162
michael@0 163 void
michael@0 164 CacheFileChunk::InitNew(CacheFileChunkListener *aCallback)
michael@0 165 {
michael@0 166 mFile->AssertOwnsLock();
michael@0 167
michael@0 168 LOG(("CacheFileChunk::InitNew() [this=%p, listener=%p]", this, aCallback));
michael@0 169
michael@0 170 MOZ_ASSERT(mState == INITIAL);
michael@0 171 MOZ_ASSERT(!mBuf);
michael@0 172 MOZ_ASSERT(!mRWBuf);
michael@0 173
michael@0 174 mBuf = static_cast<char *>(moz_xmalloc(kMinBufSize));
michael@0 175 mBufSize = kMinBufSize;
michael@0 176 mDataSize = 0;
michael@0 177 mState = READY;
michael@0 178 mIsDirty = true;
michael@0 179
michael@0 180 DoMemoryReport(MemorySize());
michael@0 181 }
michael@0 182
michael@0 183 nsresult
michael@0 184 CacheFileChunk::Read(CacheFileHandle *aHandle, uint32_t aLen,
michael@0 185 CacheHash::Hash16_t aHash,
michael@0 186 CacheFileChunkListener *aCallback)
michael@0 187 {
michael@0 188 mFile->AssertOwnsLock();
michael@0 189
michael@0 190 LOG(("CacheFileChunk::Read() [this=%p, handle=%p, len=%d, listener=%p]",
michael@0 191 this, aHandle, aLen, aCallback));
michael@0 192
michael@0 193 MOZ_ASSERT(mState == INITIAL);
michael@0 194 MOZ_ASSERT(!mBuf);
michael@0 195 MOZ_ASSERT(!mRWBuf);
michael@0 196 MOZ_ASSERT(aLen);
michael@0 197
michael@0 198 nsresult rv;
michael@0 199
michael@0 200 mRWBuf = static_cast<char *>(moz_xmalloc(aLen));
michael@0 201 mRWBufSize = aLen;
michael@0 202
michael@0 203 DoMemoryReport(MemorySize());
michael@0 204
michael@0 205 rv = CacheFileIOManager::Read(aHandle, mIndex * kChunkSize, mRWBuf, aLen,
michael@0 206 true, this);
michael@0 207 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 208 rv = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
michael@0 209 SetError(rv);
michael@0 210 } else {
michael@0 211 mState = READING;
michael@0 212 mListener = aCallback;
michael@0 213 mDataSize = aLen;
michael@0 214 mReadHash = aHash;
michael@0 215 }
michael@0 216
michael@0 217 return rv;
michael@0 218 }
michael@0 219
michael@0 220 nsresult
michael@0 221 CacheFileChunk::Write(CacheFileHandle *aHandle,
michael@0 222 CacheFileChunkListener *aCallback)
michael@0 223 {
michael@0 224 mFile->AssertOwnsLock();
michael@0 225
michael@0 226 LOG(("CacheFileChunk::Write() [this=%p, handle=%p, listener=%p]",
michael@0 227 this, aHandle, aCallback));
michael@0 228
michael@0 229 MOZ_ASSERT(mState == READY);
michael@0 230 MOZ_ASSERT(!mRWBuf);
michael@0 231 MOZ_ASSERT(mBuf);
michael@0 232 MOZ_ASSERT(mDataSize); // Don't write chunk when it is empty
michael@0 233
michael@0 234 nsresult rv;
michael@0 235
michael@0 236 mRWBuf = mBuf;
michael@0 237 mRWBufSize = mBufSize;
michael@0 238 mBuf = nullptr;
michael@0 239 mBufSize = 0;
michael@0 240
michael@0 241 rv = CacheFileIOManager::Write(aHandle, mIndex * kChunkSize, mRWBuf,
michael@0 242 mDataSize, false, this);
michael@0 243 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 244 SetError(rv);
michael@0 245 } else {
michael@0 246 mState = WRITING;
michael@0 247 mListener = aCallback;
michael@0 248 mIsDirty = false;
michael@0 249 }
michael@0 250
michael@0 251 return rv;
michael@0 252 }
michael@0 253
michael@0 254 void
michael@0 255 CacheFileChunk::WaitForUpdate(CacheFileChunkListener *aCallback)
michael@0 256 {
michael@0 257 mFile->AssertOwnsLock();
michael@0 258
michael@0 259 LOG(("CacheFileChunk::WaitForUpdate() [this=%p, listener=%p]",
michael@0 260 this, aCallback));
michael@0 261
michael@0 262 MOZ_ASSERT(mFile->mOutput);
michael@0 263 MOZ_ASSERT(IsReady());
michael@0 264
michael@0 265 #ifdef DEBUG
michael@0 266 for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
michael@0 267 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
michael@0 268 }
michael@0 269 #endif
michael@0 270
michael@0 271 ChunkListenerItem *item = new ChunkListenerItem();
michael@0 272 item->mTarget = NS_GetCurrentThread();
michael@0 273 item->mCallback = aCallback;
michael@0 274
michael@0 275 mUpdateListeners.AppendElement(item);
michael@0 276 }
michael@0 277
michael@0 278 nsresult
michael@0 279 CacheFileChunk::CancelWait(CacheFileChunkListener *aCallback)
michael@0 280 {
michael@0 281 mFile->AssertOwnsLock();
michael@0 282
michael@0 283 LOG(("CacheFileChunk::CancelWait() [this=%p, listener=%p]", this, aCallback));
michael@0 284
michael@0 285 MOZ_ASSERT(IsReady());
michael@0 286
michael@0 287 uint32_t i;
michael@0 288 for (i = 0 ; i < mUpdateListeners.Length() ; i++) {
michael@0 289 ChunkListenerItem *item = mUpdateListeners[i];
michael@0 290
michael@0 291 if (item->mCallback == aCallback) {
michael@0 292 mUpdateListeners.RemoveElementAt(i);
michael@0 293 delete item;
michael@0 294 break;
michael@0 295 }
michael@0 296 }
michael@0 297
michael@0 298 #ifdef DEBUG
michael@0 299 for ( ; i < mUpdateListeners.Length() ; i++) {
michael@0 300 MOZ_ASSERT(mUpdateListeners[i]->mCallback != aCallback);
michael@0 301 }
michael@0 302 #endif
michael@0 303
michael@0 304 return NS_OK;
michael@0 305 }
michael@0 306
michael@0 307 nsresult
michael@0 308 CacheFileChunk::NotifyUpdateListeners()
michael@0 309 {
michael@0 310 mFile->AssertOwnsLock();
michael@0 311
michael@0 312 LOG(("CacheFileChunk::NotifyUpdateListeners() [this=%p]", this));
michael@0 313
michael@0 314 MOZ_ASSERT(IsReady());
michael@0 315
michael@0 316 nsresult rv, rv2;
michael@0 317
michael@0 318 rv = NS_OK;
michael@0 319 for (uint32_t i = 0 ; i < mUpdateListeners.Length() ; i++) {
michael@0 320 ChunkListenerItem *item = mUpdateListeners[i];
michael@0 321
michael@0 322 LOG(("CacheFileChunk::NotifyUpdateListeners() - Notifying listener %p "
michael@0 323 "[this=%p]", item->mCallback.get(), this));
michael@0 324
michael@0 325 nsRefPtr<NotifyUpdateListenerEvent> ev;
michael@0 326 ev = new NotifyUpdateListenerEvent(item->mCallback, this);
michael@0 327 rv2 = item->mTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
michael@0 328 if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
michael@0 329 rv = rv2;
michael@0 330 delete item;
michael@0 331 }
michael@0 332
michael@0 333 mUpdateListeners.Clear();
michael@0 334
michael@0 335 return rv;
michael@0 336 }
michael@0 337
michael@0 338 uint32_t
michael@0 339 CacheFileChunk::Index()
michael@0 340 {
michael@0 341 return mIndex;
michael@0 342 }
michael@0 343
michael@0 344 CacheHash::Hash16_t
michael@0 345 CacheFileChunk::Hash()
michael@0 346 {
michael@0 347 mFile->AssertOwnsLock();
michael@0 348
michael@0 349 MOZ_ASSERT(mBuf);
michael@0 350 MOZ_ASSERT(!mListener);
michael@0 351 MOZ_ASSERT(IsReady());
michael@0 352
michael@0 353 return CacheHash::Hash16(BufForReading(), mDataSize);
michael@0 354 }
michael@0 355
michael@0 356 uint32_t
michael@0 357 CacheFileChunk::DataSize()
michael@0 358 {
michael@0 359 mFile->AssertOwnsLock();
michael@0 360 return mDataSize;
michael@0 361 }
michael@0 362
michael@0 363 void
michael@0 364 CacheFileChunk::UpdateDataSize(uint32_t aOffset, uint32_t aLen, bool aEOF)
michael@0 365 {
michael@0 366 mFile->AssertOwnsLock();
michael@0 367
michael@0 368 MOZ_ASSERT(!aEOF, "Implement me! What to do with opened streams?");
michael@0 369 MOZ_ASSERT(aOffset <= mDataSize);
michael@0 370
michael@0 371 // UpdateDataSize() is called only when we've written some data to the chunk
michael@0 372 // and we never write data anymore once some error occurs.
michael@0 373 MOZ_ASSERT(mState != ERROR);
michael@0 374
michael@0 375 LOG(("CacheFileChunk::UpdateDataSize() [this=%p, offset=%d, len=%d, EOF=%d]",
michael@0 376 this, aOffset, aLen, aEOF));
michael@0 377
michael@0 378 mIsDirty = true;
michael@0 379
michael@0 380 int64_t fileSize = kChunkSize * mIndex + aOffset + aLen;
michael@0 381 bool notify = false;
michael@0 382
michael@0 383 if (fileSize > mFile->mDataSize)
michael@0 384 mFile->mDataSize = fileSize;
michael@0 385
michael@0 386 if (aOffset + aLen > mDataSize) {
michael@0 387 mDataSize = aOffset + aLen;
michael@0 388 notify = true;
michael@0 389 }
michael@0 390
michael@0 391 if (mState == READY || mState == WRITING) {
michael@0 392 MOZ_ASSERT(mValidityMap.Length() == 0);
michael@0 393
michael@0 394 if (notify)
michael@0 395 NotifyUpdateListeners();
michael@0 396
michael@0 397 return;
michael@0 398 }
michael@0 399
michael@0 400 // We're still waiting for data from the disk. This chunk cannot be used by
michael@0 401 // input stream, so there must be no update listener. We also need to keep
michael@0 402 // track of where the data is written so that we can correctly merge the new
michael@0 403 // data with the old one.
michael@0 404
michael@0 405 MOZ_ASSERT(mUpdateListeners.Length() == 0);
michael@0 406 MOZ_ASSERT(mState == READING);
michael@0 407
michael@0 408 ValidityPair pair(aOffset, aLen);
michael@0 409
michael@0 410 if (mValidityMap.Length() == 0) {
michael@0 411 mValidityMap.AppendElement(pair);
michael@0 412 return;
michael@0 413 }
michael@0 414
michael@0 415
michael@0 416 // Find out where to place this pair into the map, it can overlap with
michael@0 417 // one preceding pair and all subsequent pairs.
michael@0 418 uint32_t pos = 0;
michael@0 419 for (pos = mValidityMap.Length() ; pos > 0 ; pos--) {
michael@0 420 if (mValidityMap[pos-1].LessThan(pair)) {
michael@0 421 if (mValidityMap[pos-1].Overlaps(pair)) {
michael@0 422 // Merge with the preceding pair
michael@0 423 mValidityMap[pos-1].Merge(pair);
michael@0 424 pos--; // Point to the updated pair
michael@0 425 }
michael@0 426 else {
michael@0 427 if (pos == mValidityMap.Length())
michael@0 428 mValidityMap.AppendElement(pair);
michael@0 429 else
michael@0 430 mValidityMap.InsertElementAt(pos, pair);
michael@0 431 }
michael@0 432
michael@0 433 break;
michael@0 434 }
michael@0 435 }
michael@0 436
michael@0 437 if (!pos)
michael@0 438 mValidityMap.InsertElementAt(0, pair);
michael@0 439
michael@0 440 // Now pos points to merged or inserted pair, check whether it overlaps with
michael@0 441 // subsequent pairs.
michael@0 442 while (pos + 1 < mValidityMap.Length()) {
michael@0 443 if (mValidityMap[pos].Overlaps(mValidityMap[pos + 1])) {
michael@0 444 mValidityMap[pos].Merge(mValidityMap[pos + 1]);
michael@0 445 mValidityMap.RemoveElementAt(pos + 1);
michael@0 446 }
michael@0 447 else {
michael@0 448 break;
michael@0 449 }
michael@0 450 }
michael@0 451 }
michael@0 452
michael@0 453 nsresult
michael@0 454 CacheFileChunk::OnFileOpened(CacheFileHandle *aHandle, nsresult aResult)
michael@0 455 {
michael@0 456 MOZ_CRASH("CacheFileChunk::OnFileOpened should not be called!");
michael@0 457 return NS_ERROR_UNEXPECTED;
michael@0 458 }
michael@0 459
michael@0 460 nsresult
michael@0 461 CacheFileChunk::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
michael@0 462 nsresult aResult)
michael@0 463 {
michael@0 464 LOG(("CacheFileChunk::OnDataWritten() [this=%p, handle=%p, result=0x%08x]",
michael@0 465 this, aHandle, aResult));
michael@0 466
michael@0 467 nsCOMPtr<CacheFileChunkListener> listener;
michael@0 468
michael@0 469 {
michael@0 470 CacheFileAutoLock lock(mFile);
michael@0 471
michael@0 472 MOZ_ASSERT(mState == WRITING);
michael@0 473 MOZ_ASSERT(mListener);
michael@0 474
michael@0 475 if (NS_WARN_IF(NS_FAILED(aResult))) {
michael@0 476 SetError(aResult);
michael@0 477 } else {
michael@0 478 mState = READY;
michael@0 479 }
michael@0 480
michael@0 481 if (!mBuf) {
michael@0 482 mBuf = mRWBuf;
michael@0 483 mBufSize = mRWBufSize;
michael@0 484 } else {
michael@0 485 free(mRWBuf);
michael@0 486 }
michael@0 487
michael@0 488 mRWBuf = nullptr;
michael@0 489 mRWBufSize = 0;
michael@0 490
michael@0 491 DoMemoryReport(MemorySize());
michael@0 492
michael@0 493 mListener.swap(listener);
michael@0 494 }
michael@0 495
michael@0 496 listener->OnChunkWritten(aResult, this);
michael@0 497
michael@0 498 return NS_OK;
michael@0 499 }
michael@0 500
michael@0 501 nsresult
michael@0 502 CacheFileChunk::OnDataRead(CacheFileHandle *aHandle, char *aBuf,
michael@0 503 nsresult aResult)
michael@0 504 {
michael@0 505 LOG(("CacheFileChunk::OnDataRead() [this=%p, handle=%p, result=0x%08x]",
michael@0 506 this, aHandle, aResult));
michael@0 507
michael@0 508 nsCOMPtr<CacheFileChunkListener> listener;
michael@0 509
michael@0 510 {
michael@0 511 CacheFileAutoLock lock(mFile);
michael@0 512
michael@0 513 MOZ_ASSERT(mState == READING);
michael@0 514 MOZ_ASSERT(mListener);
michael@0 515
michael@0 516 if (NS_SUCCEEDED(aResult)) {
michael@0 517 CacheHash::Hash16_t hash = CacheHash::Hash16(mRWBuf, mRWBufSize);
michael@0 518 if (hash != mReadHash) {
michael@0 519 LOG(("CacheFileChunk::OnDataRead() - Hash mismatch! Hash of the data is"
michael@0 520 " %hx, hash in metadata is %hx. [this=%p, idx=%d]",
michael@0 521 hash, mReadHash, this, mIndex));
michael@0 522 aResult = NS_ERROR_FILE_CORRUPTED;
michael@0 523 }
michael@0 524 else {
michael@0 525 if (!mBuf) {
michael@0 526 // Just swap the buffers if we don't have mBuf yet
michael@0 527 MOZ_ASSERT(mDataSize == mRWBufSize);
michael@0 528 mBuf = mRWBuf;
michael@0 529 mBufSize = mRWBufSize;
michael@0 530 mRWBuf = nullptr;
michael@0 531 mRWBufSize = 0;
michael@0 532 } else {
michael@0 533 // Merge data with write buffer
michael@0 534 if (mRWBufSize < mBufSize) {
michael@0 535 mRWBuf = static_cast<char *>(moz_xrealloc(mRWBuf, mBufSize));
michael@0 536 mRWBufSize = mBufSize;
michael@0 537 }
michael@0 538
michael@0 539 for (uint32_t i = 0 ; i < mValidityMap.Length() ; i++) {
michael@0 540 memcpy(mRWBuf + mValidityMap[i].Offset(),
michael@0 541 mBuf + mValidityMap[i].Offset(), mValidityMap[i].Len());
michael@0 542 }
michael@0 543
michael@0 544 free(mBuf);
michael@0 545 mBuf = mRWBuf;
michael@0 546 mBufSize = mRWBufSize;
michael@0 547 mRWBuf = nullptr;
michael@0 548 mRWBufSize = 0;
michael@0 549
michael@0 550 DoMemoryReport(MemorySize());
michael@0 551 }
michael@0 552 }
michael@0 553 }
michael@0 554
michael@0 555 if (NS_FAILED(aResult)) {
michael@0 556 aResult = mIndex ? NS_ERROR_FILE_CORRUPTED : NS_ERROR_FILE_NOT_FOUND;
michael@0 557 SetError(aResult);
michael@0 558 mDataSize = 0;
michael@0 559 } else {
michael@0 560 mState = READY;
michael@0 561 }
michael@0 562
michael@0 563 mListener.swap(listener);
michael@0 564 }
michael@0 565
michael@0 566 listener->OnChunkRead(aResult, this);
michael@0 567
michael@0 568 return NS_OK;
michael@0 569 }
michael@0 570
michael@0 571 nsresult
michael@0 572 CacheFileChunk::OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult)
michael@0 573 {
michael@0 574 MOZ_CRASH("CacheFileChunk::OnFileDoomed should not be called!");
michael@0 575 return NS_ERROR_UNEXPECTED;
michael@0 576 }
michael@0 577
michael@0 578 nsresult
michael@0 579 CacheFileChunk::OnEOFSet(CacheFileHandle *aHandle, nsresult aResult)
michael@0 580 {
michael@0 581 MOZ_CRASH("CacheFileChunk::OnEOFSet should not be called!");
michael@0 582 return NS_ERROR_UNEXPECTED;
michael@0 583 }
michael@0 584
michael@0 585 nsresult
michael@0 586 CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
michael@0 587 {
michael@0 588 MOZ_CRASH("CacheFileChunk::OnFileRenamed should not be called!");
michael@0 589 return NS_ERROR_UNEXPECTED;
michael@0 590 }
michael@0 591
michael@0 592 bool
michael@0 593 CacheFileChunk::IsReady() const
michael@0 594 {
michael@0 595 mFile->AssertOwnsLock();
michael@0 596
michael@0 597 return (NS_SUCCEEDED(mStatus) && (mState == READY || mState == WRITING));
michael@0 598 }
michael@0 599
michael@0 600 bool
michael@0 601 CacheFileChunk::IsDirty() const
michael@0 602 {
michael@0 603 mFile->AssertOwnsLock();
michael@0 604
michael@0 605 return mIsDirty;
michael@0 606 }
michael@0 607
michael@0 608 nsresult
michael@0 609 CacheFileChunk::GetStatus()
michael@0 610 {
michael@0 611 mFile->AssertOwnsLock();
michael@0 612
michael@0 613 return mStatus;
michael@0 614 }
michael@0 615
michael@0 616 void
michael@0 617 CacheFileChunk::SetError(nsresult aStatus)
michael@0 618 {
michael@0 619 if (NS_SUCCEEDED(mStatus)) {
michael@0 620 MOZ_ASSERT(mState != ERROR);
michael@0 621 mStatus = aStatus;
michael@0 622 mState = ERROR;
michael@0 623 } else {
michael@0 624 MOZ_ASSERT(mState == ERROR);
michael@0 625 }
michael@0 626 }
michael@0 627
michael@0 628 char *
michael@0 629 CacheFileChunk::BufForWriting() const
michael@0 630 {
michael@0 631 mFile->AssertOwnsLock();
michael@0 632
michael@0 633 MOZ_ASSERT(mBuf); // Writer should always first call EnsureBufSize()
michael@0 634
michael@0 635 MOZ_ASSERT((mState == READY && !mRWBuf) ||
michael@0 636 (mState == WRITING && mRWBuf) ||
michael@0 637 (mState == READING && mRWBuf));
michael@0 638
michael@0 639 return mBuf;
michael@0 640 }
michael@0 641
michael@0 642 const char *
michael@0 643 CacheFileChunk::BufForReading() const
michael@0 644 {
michael@0 645 mFile->AssertOwnsLock();
michael@0 646
michael@0 647 MOZ_ASSERT((mState == READY && mBuf && !mRWBuf) ||
michael@0 648 (mState == WRITING && mRWBuf));
michael@0 649
michael@0 650 return mBuf ? mBuf : mRWBuf;
michael@0 651 }
michael@0 652
michael@0 653 void
michael@0 654 CacheFileChunk::EnsureBufSize(uint32_t aBufSize)
michael@0 655 {
michael@0 656 mFile->AssertOwnsLock();
michael@0 657
michael@0 658 // EnsureBufSize() is called only when we want to write some data to the chunk
michael@0 659 // and we never write data anymore once some error occurs.
michael@0 660 MOZ_ASSERT(mState != ERROR);
michael@0 661
michael@0 662 if (mBufSize >= aBufSize)
michael@0 663 return;
michael@0 664
michael@0 665 bool copy = false;
michael@0 666 if (!mBuf && mState == WRITING) {
michael@0 667 // We need to duplicate the data that is being written on the background
michael@0 668 // thread, so make sure that all the data fits into the new buffer.
michael@0 669 copy = true;
michael@0 670
michael@0 671 if (mRWBufSize > aBufSize)
michael@0 672 aBufSize = mRWBufSize;
michael@0 673 }
michael@0 674
michael@0 675 // find smallest power of 2 greater than or equal to aBufSize
michael@0 676 aBufSize--;
michael@0 677 aBufSize |= aBufSize >> 1;
michael@0 678 aBufSize |= aBufSize >> 2;
michael@0 679 aBufSize |= aBufSize >> 4;
michael@0 680 aBufSize |= aBufSize >> 8;
michael@0 681 aBufSize |= aBufSize >> 16;
michael@0 682 aBufSize++;
michael@0 683
michael@0 684 const uint32_t minBufSize = kMinBufSize;
michael@0 685 const uint32_t maxBufSize = kChunkSize;
michael@0 686 aBufSize = clamped(aBufSize, minBufSize, maxBufSize);
michael@0 687
michael@0 688 mBuf = static_cast<char *>(moz_xrealloc(mBuf, aBufSize));
michael@0 689 mBufSize = aBufSize;
michael@0 690
michael@0 691 if (copy)
michael@0 692 memcpy(mBuf, mRWBuf, mRWBufSize);
michael@0 693
michael@0 694 DoMemoryReport(MemorySize());
michael@0 695 }
michael@0 696
michael@0 697 // Memory reporting
michael@0 698
michael@0 699 size_t
michael@0 700 CacheFileChunk::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 701 {
michael@0 702 size_t n = 0;
michael@0 703 n += mallocSizeOf(mBuf);
michael@0 704 n += mallocSizeOf(mRWBuf);
michael@0 705 n += mValidityMap.SizeOfExcludingThis(mallocSizeOf);
michael@0 706
michael@0 707 return n;
michael@0 708 }
michael@0 709
michael@0 710 size_t
michael@0 711 CacheFileChunk::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 712 {
michael@0 713 return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
michael@0 714 }
michael@0 715
michael@0 716 } // net
michael@0 717 } // mozilla

mercurial