nsDiskCacheMap.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* vim:set ts=4 sw=4 sts=4 cin et: */
  3. /* This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. #ifndef _nsDiskCacheMap_h_
  7. #define _nsDiskCacheMap_h_
  8. #include "mozilla/MemoryReporting.h"
  9. #include <limits.h>
  10. #include "prnetdb.h"
  11. #include "nsDebug.h"
  12. #include "nsError.h"
  13. #include "nsIFile.h"
  14. #include "nsITimer.h"
  15. #include "nsDiskCache.h"
  16. #include "nsDiskCacheBlockFile.h"
  17. class nsDiskCacheBinding;
  18. struct nsDiskCacheEntry;
  19. /******************************************************************************
  20. * nsDiskCacheRecord
  21. *
  22. * Cache Location Format
  23. *
  24. * 1000 0000 0000 0000 0000 0000 0000 0000 : initialized bit
  25. *
  26. * 0011 0000 0000 0000 0000 0000 0000 0000 : File Selector (0 = separate file)
  27. * 0000 0011 0000 0000 0000 0000 0000 0000 : number of extra contiguous blocks 1-4
  28. * 0100 1100 0000 0000 0000 0000 0000 0000 : reserved bits
  29. * 0000 0000 1111 1111 1111 1111 1111 1111 : block# 0-16777216 (2^24)
  30. *
  31. * 0000 0000 1111 1111 1111 1111 0000 0000 : eFileSizeMask (size of file in k: see note)
  32. * 0000 0000 0000 0000 0000 0000 1111 1111 : eFileGenerationMask
  33. *
  34. * File Selector:
  35. * 0 = separate file on disk
  36. * 1 = 256 byte block file
  37. * 2 = 1k block file
  38. * 3 = 4k block file
  39. *
  40. * eFileSizeMask note: Files larger than 65535 KiB have this limit stored in
  41. * the location. The file itself must be examined to
  42. * determine its actual size if necessary.
  43. *
  44. *****************************************************************************/
  45. /*
  46. We have 3 block files with roughly the same max size (32MB)
  47. 1 - block size 256B, number of blocks 131072
  48. 2 - block size 1kB, number of blocks 32768
  49. 3 - block size 4kB, number of blocks 8192
  50. */
  51. #define kNumBlockFiles 3
  52. #define SIZE_SHIFT(idx) (2 * ((idx) - 1))
  53. #define BLOCK_SIZE_FOR_INDEX(idx) ((idx) ? (256 << SIZE_SHIFT(idx)) : 0)
  54. #define BITMAP_SIZE_FOR_INDEX(idx) ((idx) ? (131072 >> SIZE_SHIFT(idx)) : 0)
  55. // Min and max values for the number of records in the DiskCachemap
  56. #define kMinRecordCount 512
  57. #define kSeparateFile 0
  58. #define kBuckets (1 << 5) // must be a power of 2!
  59. // Maximum size in K which can be stored in the location (see eFileSizeMask).
  60. // Both data and metadata can be larger, but only up to kMaxDataSizeK can be
  61. // counted into total cache size. I.e. if there are entries where either data or
  62. // metadata is larger than kMaxDataSizeK, the total cache size will be
  63. // inaccurate (smaller) than the actual cache size. The alternative is to stat
  64. // the files to find the real size, which was decided against for performance
  65. // reasons. See bug #651100 comment #21.
  66. #define kMaxDataSizeK 0xFFFF
  67. // preallocate up to 1MB of separate cache file
  68. #define kPreallocateLimit 1 * 1024 * 1024
  69. // The minimum amount of milliseconds to wait before re-attempting to
  70. // revalidate the cache.
  71. #define kRevalidateCacheTimeout 3000
  72. #define kRevalidateCacheTimeoutTolerance 10
  73. #define kRevalidateCacheErrorTimeout 1000
  74. class nsDiskCacheRecord {
  75. private:
  76. uint32_t mHashNumber;
  77. uint32_t mEvictionRank;
  78. uint32_t mDataLocation;
  79. uint32_t mMetaLocation;
  80. enum {
  81. eLocationInitializedMask = 0x80000000,
  82. eLocationSelectorMask = 0x30000000,
  83. eLocationSelectorOffset = 28,
  84. eExtraBlocksMask = 0x03000000,
  85. eExtraBlocksOffset = 24,
  86. eReservedMask = 0x4C000000,
  87. eBlockNumberMask = 0x00FFFFFF,
  88. eFileSizeMask = 0x00FFFF00,
  89. eFileSizeOffset = 8,
  90. eFileGenerationMask = 0x000000FF,
  91. eFileReservedMask = 0x4F000000
  92. };
  93. public:
  94. nsDiskCacheRecord()
  95. : mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
  96. {
  97. }
  98. bool ValidRecord()
  99. {
  100. if ((mDataLocation & eReservedMask) || (mMetaLocation & eReservedMask))
  101. return false;
  102. return true;
  103. }
  104. // HashNumber accessors
  105. uint32_t HashNumber() const { return mHashNumber; }
  106. void SetHashNumber( uint32_t hashNumber) { mHashNumber = hashNumber; }
  107. // EvictionRank accessors
  108. uint32_t EvictionRank() const { return mEvictionRank; }
  109. void SetEvictionRank( uint32_t rank) { mEvictionRank = rank ? rank : 1; }
  110. // DataLocation accessors
  111. bool DataLocationInitialized() const { return 0 != (mDataLocation & eLocationInitializedMask); }
  112. void ClearDataLocation() { mDataLocation = 0; }
  113. uint32_t DataFile() const
  114. {
  115. return (uint32_t)(mDataLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
  116. }
  117. void SetDataBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
  118. {
  119. // clear everything
  120. mDataLocation = 0;
  121. // set file index
  122. NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
  123. NS_ASSERTION( index > 0,"invalid location index");
  124. mDataLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
  125. // set startBlock
  126. NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
  127. mDataLocation |= startBlock & eBlockNumberMask;
  128. // set blockCount
  129. NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
  130. --blockCount;
  131. mDataLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
  132. mDataLocation |= eLocationInitializedMask;
  133. }
  134. uint32_t DataBlockCount() const
  135. {
  136. return (uint32_t)((mDataLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
  137. }
  138. uint32_t DataStartBlock() const
  139. {
  140. return (mDataLocation & eBlockNumberMask);
  141. }
  142. uint32_t DataBlockSize() const
  143. {
  144. return BLOCK_SIZE_FOR_INDEX(DataFile());
  145. }
  146. uint32_t DataFileSize() const { return (mDataLocation & eFileSizeMask) >> eFileSizeOffset; }
  147. void SetDataFileSize(uint32_t size)
  148. {
  149. NS_ASSERTION((mDataLocation & eFileReservedMask) == 0, "bad location");
  150. mDataLocation &= ~eFileSizeMask; // clear eFileSizeMask
  151. mDataLocation |= (size << eFileSizeOffset) & eFileSizeMask;
  152. }
  153. uint8_t DataFileGeneration() const
  154. {
  155. return (mDataLocation & eFileGenerationMask);
  156. }
  157. void SetDataFileGeneration( uint8_t generation)
  158. {
  159. // clear everything, (separate file index = 0)
  160. mDataLocation = 0;
  161. mDataLocation |= generation & eFileGenerationMask;
  162. mDataLocation |= eLocationInitializedMask;
  163. }
  164. // MetaLocation accessors
  165. bool MetaLocationInitialized() const { return 0 != (mMetaLocation & eLocationInitializedMask); }
  166. void ClearMetaLocation() { mMetaLocation = 0; }
  167. uint32_t MetaLocation() const { return mMetaLocation; }
  168. uint32_t MetaFile() const
  169. {
  170. return (uint32_t)(mMetaLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
  171. }
  172. void SetMetaBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
  173. {
  174. // clear everything
  175. mMetaLocation = 0;
  176. // set file index
  177. NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
  178. NS_ASSERTION( index > 0, "invalid location index");
  179. mMetaLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
  180. // set startBlock
  181. NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
  182. mMetaLocation |= startBlock & eBlockNumberMask;
  183. // set blockCount
  184. NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
  185. --blockCount;
  186. mMetaLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
  187. mMetaLocation |= eLocationInitializedMask;
  188. }
  189. uint32_t MetaBlockCount() const
  190. {
  191. return (uint32_t)((mMetaLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
  192. }
  193. uint32_t MetaStartBlock() const
  194. {
  195. return (mMetaLocation & eBlockNumberMask);
  196. }
  197. uint32_t MetaBlockSize() const
  198. {
  199. return BLOCK_SIZE_FOR_INDEX(MetaFile());
  200. }
  201. uint32_t MetaFileSize() const { return (mMetaLocation & eFileSizeMask) >> eFileSizeOffset; }
  202. void SetMetaFileSize(uint32_t size)
  203. {
  204. mMetaLocation &= ~eFileSizeMask; // clear eFileSizeMask
  205. mMetaLocation |= (size << eFileSizeOffset) & eFileSizeMask;
  206. }
  207. uint8_t MetaFileGeneration() const
  208. {
  209. return (mMetaLocation & eFileGenerationMask);
  210. }
  211. void SetMetaFileGeneration( uint8_t generation)
  212. {
  213. // clear everything, (separate file index = 0)
  214. mMetaLocation = 0;
  215. mMetaLocation |= generation & eFileGenerationMask;
  216. mMetaLocation |= eLocationInitializedMask;
  217. }
  218. uint8_t Generation() const
  219. {
  220. if ((mDataLocation & eLocationInitializedMask) &&
  221. (DataFile() == 0))
  222. return DataFileGeneration();
  223. if ((mMetaLocation & eLocationInitializedMask) &&
  224. (MetaFile() == 0))
  225. return MetaFileGeneration();
  226. return 0; // no generation
  227. }
  228. #if defined(IS_LITTLE_ENDIAN)
  229. void Swap()
  230. {
  231. mHashNumber = htonl(mHashNumber);
  232. mEvictionRank = htonl(mEvictionRank);
  233. mDataLocation = htonl(mDataLocation);
  234. mMetaLocation = htonl(mMetaLocation);
  235. }
  236. #endif
  237. #if defined(IS_LITTLE_ENDIAN)
  238. void Unswap()
  239. {
  240. mHashNumber = ntohl(mHashNumber);
  241. mEvictionRank = ntohl(mEvictionRank);
  242. mDataLocation = ntohl(mDataLocation);
  243. mMetaLocation = ntohl(mMetaLocation);
  244. }
  245. #endif
  246. };
  247. /******************************************************************************
  248. * nsDiskCacheRecordVisitor
  249. *****************************************************************************/
  250. enum { kDeleteRecordAndContinue = -1,
  251. kStopVisitingRecords = 0,
  252. kVisitNextRecord = 1
  253. };
  254. class nsDiskCacheRecordVisitor {
  255. public:
  256. virtual int32_t VisitRecord( nsDiskCacheRecord * mapRecord) = 0;
  257. };
  258. /******************************************************************************
  259. * nsDiskCacheHeader
  260. *****************************************************************************/
  261. struct nsDiskCacheHeader {
  262. uint32_t mVersion; // cache version.
  263. uint32_t mDataSize; // size of cache in units of 1024bytes.
  264. int32_t mEntryCount; // number of entries stored in cache.
  265. uint32_t mIsDirty; // dirty flag.
  266. int32_t mRecordCount; // Number of records
  267. uint32_t mEvictionRank[kBuckets]; // Highest EvictionRank of the bucket
  268. uint32_t mBucketUsage[kBuckets]; // Number of used entries in the bucket
  269. nsDiskCacheHeader()
  270. : mVersion(nsDiskCache::kCurrentVersion)
  271. , mDataSize(0)
  272. , mEntryCount(0)
  273. , mIsDirty(true)
  274. , mRecordCount(0)
  275. {}
  276. void Swap()
  277. {
  278. #if defined(IS_LITTLE_ENDIAN)
  279. mVersion = htonl(mVersion);
  280. mDataSize = htonl(mDataSize);
  281. mEntryCount = htonl(mEntryCount);
  282. mIsDirty = htonl(mIsDirty);
  283. mRecordCount = htonl(mRecordCount);
  284. for (uint32_t i = 0; i < kBuckets ; i++) {
  285. mEvictionRank[i] = htonl(mEvictionRank[i]);
  286. mBucketUsage[i] = htonl(mBucketUsage[i]);
  287. }
  288. #endif
  289. }
  290. void Unswap()
  291. {
  292. #if defined(IS_LITTLE_ENDIAN)
  293. mVersion = ntohl(mVersion);
  294. mDataSize = ntohl(mDataSize);
  295. mEntryCount = ntohl(mEntryCount);
  296. mIsDirty = ntohl(mIsDirty);
  297. mRecordCount = ntohl(mRecordCount);
  298. for (uint32_t i = 0; i < kBuckets ; i++) {
  299. mEvictionRank[i] = ntohl(mEvictionRank[i]);
  300. mBucketUsage[i] = ntohl(mBucketUsage[i]);
  301. }
  302. #endif
  303. }
  304. };
  305. /******************************************************************************
  306. * nsDiskCacheMap
  307. *****************************************************************************/
  308. class nsDiskCacheMap {
  309. public:
  310. nsDiskCacheMap() :
  311. mCacheDirectory(nullptr),
  312. mMapFD(nullptr),
  313. mCleanFD(nullptr),
  314. mRecordArray(nullptr),
  315. mBufferSize(0),
  316. mBuffer(nullptr),
  317. mMaxRecordCount(16384), // this default value won't matter
  318. mIsDirtyCacheFlushed(false),
  319. mLastInvalidateTime(0)
  320. { }
  321. ~nsDiskCacheMap()
  322. {
  323. (void) Close(true);
  324. }
  325. /**
  326. * File Operations
  327. *
  328. * Open
  329. *
  330. * Creates a new cache map file if one doesn't exist.
  331. * Returns error if it detects change in format or cache wasn't closed.
  332. */
  333. nsresult Open( nsIFile * cacheDirectory,
  334. nsDiskCache::CorruptCacheInfo * corruptInfo);
  335. nsresult Close(bool flush);
  336. nsresult Trim();
  337. nsresult FlushHeader();
  338. nsresult FlushRecords( bool unswap);
  339. void NotifyCapacityChange(uint32_t capacity);
  340. /**
  341. * Record operations
  342. */
  343. nsresult AddRecord( nsDiskCacheRecord * mapRecord, nsDiskCacheRecord * oldRecord);
  344. nsresult UpdateRecord( nsDiskCacheRecord * mapRecord);
  345. nsresult FindRecord( uint32_t hashNumber, nsDiskCacheRecord * mapRecord);
  346. nsresult DeleteRecord( nsDiskCacheRecord * mapRecord);
  347. nsresult VisitRecords( nsDiskCacheRecordVisitor * visitor);
  348. nsresult EvictRecords( nsDiskCacheRecordVisitor * visitor);
  349. /**
  350. * Disk Entry operations
  351. */
  352. nsresult DeleteStorage( nsDiskCacheRecord * record);
  353. nsresult GetFileForDiskCacheRecord( nsDiskCacheRecord * record,
  354. bool meta,
  355. bool createPath,
  356. nsIFile ** result);
  357. nsresult GetLocalFileForDiskCacheRecord( nsDiskCacheRecord * record,
  358. bool meta,
  359. bool createPath,
  360. nsIFile ** result);
  361. // On success, this returns the buffer owned by nsDiskCacheMap,
  362. // so it must not be deleted by the caller.
  363. nsDiskCacheEntry * ReadDiskCacheEntry( nsDiskCacheRecord * record);
  364. nsresult WriteDiskCacheEntry( nsDiskCacheBinding * binding);
  365. nsresult ReadDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
  366. nsresult WriteDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
  367. nsresult DeleteStorage( nsDiskCacheRecord * record, bool metaData);
  368. /**
  369. * Statistical Operations
  370. */
  371. void IncrementTotalSize( uint32_t delta)
  372. {
  373. mHeader.mDataSize += delta;
  374. mHeader.mIsDirty = true;
  375. }
  376. void DecrementTotalSize( uint32_t delta)
  377. {
  378. NS_ASSERTION(mHeader.mDataSize >= delta, "disk cache size negative?");
  379. mHeader.mDataSize = mHeader.mDataSize > delta ? mHeader.mDataSize - delta : 0;
  380. mHeader.mIsDirty = true;
  381. }
  382. inline void IncrementTotalSize( uint32_t blocks, uint32_t blockSize)
  383. {
  384. // Round up to nearest K
  385. IncrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
  386. }
  387. inline void DecrementTotalSize( uint32_t blocks, uint32_t blockSize)
  388. {
  389. // Round up to nearest K
  390. DecrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
  391. }
  392. uint32_t TotalSize() { return mHeader.mDataSize; }
  393. int32_t EntryCount() { return mHeader.mEntryCount; }
  394. size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
  395. private:
  396. /**
  397. * Private methods
  398. */
  399. nsresult OpenBlockFiles(nsDiskCache::CorruptCacheInfo * corruptInfo);
  400. nsresult CloseBlockFiles(bool flush);
  401. bool CacheFilesExist();
  402. nsresult CreateCacheSubDirectories();
  403. uint32_t CalculateFileIndex(uint32_t size);
  404. nsresult GetBlockFileForIndex( uint32_t index, nsIFile ** result);
  405. uint32_t GetBlockSizeForIndex( uint32_t index) const {
  406. return BLOCK_SIZE_FOR_INDEX(index);
  407. }
  408. uint32_t GetBitMapSizeForIndex( uint32_t index) const {
  409. return BITMAP_SIZE_FOR_INDEX(index);
  410. }
  411. // returns the bucket number
  412. uint32_t GetBucketIndex( uint32_t hashNumber) const {
  413. return (hashNumber & (kBuckets - 1));
  414. }
  415. // Gets the size of the bucket (in number of records)
  416. uint32_t GetRecordsPerBucket() const {
  417. return mHeader.mRecordCount / kBuckets;
  418. }
  419. // Gets the first record in the bucket
  420. nsDiskCacheRecord *GetFirstRecordInBucket(uint32_t bucket) const {
  421. return mRecordArray + bucket * GetRecordsPerBucket();
  422. }
  423. uint32_t GetBucketRank(uint32_t bucketIndex, uint32_t targetRank);
  424. int32_t VisitEachRecord(uint32_t bucketIndex,
  425. nsDiskCacheRecordVisitor * visitor,
  426. uint32_t evictionRank);
  427. nsresult GrowRecords();
  428. nsresult ShrinkRecords();
  429. nsresult EnsureBuffer(uint32_t bufSize);
  430. // The returned structure will point to the buffer owned by nsDiskCacheMap,
  431. // so it must not be deleted by the caller.
  432. nsDiskCacheEntry * CreateDiskCacheEntry(nsDiskCacheBinding * binding,
  433. uint32_t * size);
  434. // Initializes the _CACHE_CLEAN_ related functionality
  435. nsresult InitCacheClean(nsIFile * cacheDirectory,
  436. nsDiskCache::CorruptCacheInfo * corruptInfo);
  437. // Writes out a value of '0' or '1' in the _CACHE_CLEAN_ file
  438. nsresult WriteCacheClean(bool clean);
  439. // Resets the timout for revalidating the cache
  440. nsresult ResetCacheTimer(int32_t timeout = kRevalidateCacheTimeout);
  441. // Invalidates the cache, calls WriteCacheClean and ResetCacheTimer
  442. nsresult InvalidateCache();
  443. // Determines if the cache is in a safe state
  444. bool IsCacheInSafeState();
  445. // Revalidates the cache by writting out the header, records, and finally
  446. // by calling WriteCacheClean(true).
  447. nsresult RevalidateCache();
  448. // Timer which revalidates the cache
  449. static void RevalidateTimerCallback(nsITimer *aTimer, void *arg);
  450. /**
  451. * data members
  452. */
  453. private:
  454. nsCOMPtr<nsITimer> mCleanCacheTimer;
  455. nsCOMPtr<nsIFile> mCacheDirectory;
  456. PRFileDesc * mMapFD;
  457. PRFileDesc * mCleanFD;
  458. nsDiskCacheRecord * mRecordArray;
  459. nsDiskCacheBlockFile mBlockFile[kNumBlockFiles];
  460. uint32_t mBufferSize;
  461. char * mBuffer;
  462. nsDiskCacheHeader mHeader;
  463. int32_t mMaxRecordCount;
  464. bool mIsDirtyCacheFlushed;
  465. PRIntervalTime mLastInvalidateTime;
  466. };
  467. #endif // _nsDiskCacheMap_h_