123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* vim:set ts=4 sw=4 sts=4 cin et: */
- /* 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 _nsDiskCacheMap_h_
- #define _nsDiskCacheMap_h_
- #include "mozilla/MemoryReporting.h"
- #include <limits.h>
- #include "prnetdb.h"
- #include "nsDebug.h"
- #include "nsError.h"
- #include "nsIFile.h"
- #include "nsITimer.h"
- #include "nsDiskCache.h"
- #include "nsDiskCacheBlockFile.h"
-
-
- class nsDiskCacheBinding;
- struct nsDiskCacheEntry;
- /******************************************************************************
- * nsDiskCacheRecord
- *
- * Cache Location Format
- *
- * 1000 0000 0000 0000 0000 0000 0000 0000 : initialized bit
- *
- * 0011 0000 0000 0000 0000 0000 0000 0000 : File Selector (0 = separate file)
- * 0000 0011 0000 0000 0000 0000 0000 0000 : number of extra contiguous blocks 1-4
- * 0100 1100 0000 0000 0000 0000 0000 0000 : reserved bits
- * 0000 0000 1111 1111 1111 1111 1111 1111 : block# 0-16777216 (2^24)
- *
- * 0000 0000 1111 1111 1111 1111 0000 0000 : eFileSizeMask (size of file in k: see note)
- * 0000 0000 0000 0000 0000 0000 1111 1111 : eFileGenerationMask
- *
- * File Selector:
- * 0 = separate file on disk
- * 1 = 256 byte block file
- * 2 = 1k block file
- * 3 = 4k block file
- *
- * eFileSizeMask note: Files larger than 65535 KiB have this limit stored in
- * the location. The file itself must be examined to
- * determine its actual size if necessary.
- *
- *****************************************************************************/
- /*
- We have 3 block files with roughly the same max size (32MB)
- 1 - block size 256B, number of blocks 131072
- 2 - block size 1kB, number of blocks 32768
- 3 - block size 4kB, number of blocks 8192
- */
- #define kNumBlockFiles 3
- #define SIZE_SHIFT(idx) (2 * ((idx) - 1))
- #define BLOCK_SIZE_FOR_INDEX(idx) ((idx) ? (256 << SIZE_SHIFT(idx)) : 0)
- #define BITMAP_SIZE_FOR_INDEX(idx) ((idx) ? (131072 >> SIZE_SHIFT(idx)) : 0)
- // Min and max values for the number of records in the DiskCachemap
- #define kMinRecordCount 512
- #define kSeparateFile 0
- #define kBuckets (1 << 5) // must be a power of 2!
- // Maximum size in K which can be stored in the location (see eFileSizeMask).
- // Both data and metadata can be larger, but only up to kMaxDataSizeK can be
- // counted into total cache size. I.e. if there are entries where either data or
- // metadata is larger than kMaxDataSizeK, the total cache size will be
- // inaccurate (smaller) than the actual cache size. The alternative is to stat
- // the files to find the real size, which was decided against for performance
- // reasons. See bug #651100 comment #21.
- #define kMaxDataSizeK 0xFFFF
- // preallocate up to 1MB of separate cache file
- #define kPreallocateLimit 1 * 1024 * 1024
- // The minimum amount of milliseconds to wait before re-attempting to
- // revalidate the cache.
- #define kRevalidateCacheTimeout 3000
- #define kRevalidateCacheTimeoutTolerance 10
- #define kRevalidateCacheErrorTimeout 1000
- class nsDiskCacheRecord {
- private:
- uint32_t mHashNumber;
- uint32_t mEvictionRank;
- uint32_t mDataLocation;
- uint32_t mMetaLocation;
-
- enum {
- eLocationInitializedMask = 0x80000000,
-
- eLocationSelectorMask = 0x30000000,
- eLocationSelectorOffset = 28,
-
- eExtraBlocksMask = 0x03000000,
- eExtraBlocksOffset = 24,
-
- eReservedMask = 0x4C000000,
-
- eBlockNumberMask = 0x00FFFFFF,
- eFileSizeMask = 0x00FFFF00,
- eFileSizeOffset = 8,
- eFileGenerationMask = 0x000000FF,
- eFileReservedMask = 0x4F000000
-
- };
- public:
- nsDiskCacheRecord()
- : mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
- {
- }
-
- bool ValidRecord()
- {
- if ((mDataLocation & eReservedMask) || (mMetaLocation & eReservedMask))
- return false;
- return true;
- }
-
- // HashNumber accessors
- uint32_t HashNumber() const { return mHashNumber; }
- void SetHashNumber( uint32_t hashNumber) { mHashNumber = hashNumber; }
- // EvictionRank accessors
- uint32_t EvictionRank() const { return mEvictionRank; }
- void SetEvictionRank( uint32_t rank) { mEvictionRank = rank ? rank : 1; }
- // DataLocation accessors
- bool DataLocationInitialized() const { return 0 != (mDataLocation & eLocationInitializedMask); }
- void ClearDataLocation() { mDataLocation = 0; }
-
- uint32_t DataFile() const
- {
- return (uint32_t)(mDataLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
- }
- void SetDataBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
- {
- // clear everything
- mDataLocation = 0;
-
- // set file index
- NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
- NS_ASSERTION( index > 0,"invalid location index");
- mDataLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
- // set startBlock
- NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
- mDataLocation |= startBlock & eBlockNumberMask;
-
- // set blockCount
- NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
- --blockCount;
- mDataLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
-
- mDataLocation |= eLocationInitializedMask;
- }
- uint32_t DataBlockCount() const
- {
- return (uint32_t)((mDataLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
- }
- uint32_t DataStartBlock() const
- {
- return (mDataLocation & eBlockNumberMask);
- }
-
- uint32_t DataBlockSize() const
- {
- return BLOCK_SIZE_FOR_INDEX(DataFile());
- }
-
- uint32_t DataFileSize() const { return (mDataLocation & eFileSizeMask) >> eFileSizeOffset; }
- void SetDataFileSize(uint32_t size)
- {
- NS_ASSERTION((mDataLocation & eFileReservedMask) == 0, "bad location");
- mDataLocation &= ~eFileSizeMask; // clear eFileSizeMask
- mDataLocation |= (size << eFileSizeOffset) & eFileSizeMask;
- }
- uint8_t DataFileGeneration() const
- {
- return (mDataLocation & eFileGenerationMask);
- }
- void SetDataFileGeneration( uint8_t generation)
- {
- // clear everything, (separate file index = 0)
- mDataLocation = 0;
- mDataLocation |= generation & eFileGenerationMask;
- mDataLocation |= eLocationInitializedMask;
- }
- // MetaLocation accessors
- bool MetaLocationInitialized() const { return 0 != (mMetaLocation & eLocationInitializedMask); }
- void ClearMetaLocation() { mMetaLocation = 0; }
- uint32_t MetaLocation() const { return mMetaLocation; }
-
- uint32_t MetaFile() const
- {
- return (uint32_t)(mMetaLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
- }
- void SetMetaBlocks( uint32_t index, uint32_t startBlock, uint32_t blockCount)
- {
- // clear everything
- mMetaLocation = 0;
-
- // set file index
- NS_ASSERTION( index < (kNumBlockFiles + 1), "invalid location index");
- NS_ASSERTION( index > 0, "invalid location index");
- mMetaLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
- // set startBlock
- NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
- mMetaLocation |= startBlock & eBlockNumberMask;
-
- // set blockCount
- NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
- --blockCount;
- mMetaLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
-
- mMetaLocation |= eLocationInitializedMask;
- }
- uint32_t MetaBlockCount() const
- {
- return (uint32_t)((mMetaLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
- }
- uint32_t MetaStartBlock() const
- {
- return (mMetaLocation & eBlockNumberMask);
- }
- uint32_t MetaBlockSize() const
- {
- return BLOCK_SIZE_FOR_INDEX(MetaFile());
- }
-
- uint32_t MetaFileSize() const { return (mMetaLocation & eFileSizeMask) >> eFileSizeOffset; }
- void SetMetaFileSize(uint32_t size)
- {
- mMetaLocation &= ~eFileSizeMask; // clear eFileSizeMask
- mMetaLocation |= (size << eFileSizeOffset) & eFileSizeMask;
- }
- uint8_t MetaFileGeneration() const
- {
- return (mMetaLocation & eFileGenerationMask);
- }
- void SetMetaFileGeneration( uint8_t generation)
- {
- // clear everything, (separate file index = 0)
- mMetaLocation = 0;
- mMetaLocation |= generation & eFileGenerationMask;
- mMetaLocation |= eLocationInitializedMask;
- }
- uint8_t Generation() const
- {
- if ((mDataLocation & eLocationInitializedMask) &&
- (DataFile() == 0))
- return DataFileGeneration();
-
- if ((mMetaLocation & eLocationInitializedMask) &&
- (MetaFile() == 0))
- return MetaFileGeneration();
-
- return 0; // no generation
- }
- #if defined(IS_LITTLE_ENDIAN)
- void Swap()
- {
- mHashNumber = htonl(mHashNumber);
- mEvictionRank = htonl(mEvictionRank);
- mDataLocation = htonl(mDataLocation);
- mMetaLocation = htonl(mMetaLocation);
- }
- #endif
-
- #if defined(IS_LITTLE_ENDIAN)
- void Unswap()
- {
- mHashNumber = ntohl(mHashNumber);
- mEvictionRank = ntohl(mEvictionRank);
- mDataLocation = ntohl(mDataLocation);
- mMetaLocation = ntohl(mMetaLocation);
- }
- #endif
- };
- /******************************************************************************
- * nsDiskCacheRecordVisitor
- *****************************************************************************/
- enum { kDeleteRecordAndContinue = -1,
- kStopVisitingRecords = 0,
- kVisitNextRecord = 1
- };
- class nsDiskCacheRecordVisitor {
- public:
- virtual int32_t VisitRecord( nsDiskCacheRecord * mapRecord) = 0;
- };
- /******************************************************************************
- * nsDiskCacheHeader
- *****************************************************************************/
- struct nsDiskCacheHeader {
- uint32_t mVersion; // cache version.
- uint32_t mDataSize; // size of cache in units of 1024bytes.
- int32_t mEntryCount; // number of entries stored in cache.
- uint32_t mIsDirty; // dirty flag.
- int32_t mRecordCount; // Number of records
- uint32_t mEvictionRank[kBuckets]; // Highest EvictionRank of the bucket
- uint32_t mBucketUsage[kBuckets]; // Number of used entries in the bucket
-
- nsDiskCacheHeader()
- : mVersion(nsDiskCache::kCurrentVersion)
- , mDataSize(0)
- , mEntryCount(0)
- , mIsDirty(true)
- , mRecordCount(0)
- {}
- void Swap()
- {
- #if defined(IS_LITTLE_ENDIAN)
- mVersion = htonl(mVersion);
- mDataSize = htonl(mDataSize);
- mEntryCount = htonl(mEntryCount);
- mIsDirty = htonl(mIsDirty);
- mRecordCount = htonl(mRecordCount);
- for (uint32_t i = 0; i < kBuckets ; i++) {
- mEvictionRank[i] = htonl(mEvictionRank[i]);
- mBucketUsage[i] = htonl(mBucketUsage[i]);
- }
- #endif
- }
-
- void Unswap()
- {
- #if defined(IS_LITTLE_ENDIAN)
- mVersion = ntohl(mVersion);
- mDataSize = ntohl(mDataSize);
- mEntryCount = ntohl(mEntryCount);
- mIsDirty = ntohl(mIsDirty);
- mRecordCount = ntohl(mRecordCount);
- for (uint32_t i = 0; i < kBuckets ; i++) {
- mEvictionRank[i] = ntohl(mEvictionRank[i]);
- mBucketUsage[i] = ntohl(mBucketUsage[i]);
- }
- #endif
- }
- };
- /******************************************************************************
- * nsDiskCacheMap
- *****************************************************************************/
- class nsDiskCacheMap {
- public:
- nsDiskCacheMap() :
- mCacheDirectory(nullptr),
- mMapFD(nullptr),
- mCleanFD(nullptr),
- mRecordArray(nullptr),
- mBufferSize(0),
- mBuffer(nullptr),
- mMaxRecordCount(16384), // this default value won't matter
- mIsDirtyCacheFlushed(false),
- mLastInvalidateTime(0)
- { }
- ~nsDiskCacheMap()
- {
- (void) Close(true);
- }
- /**
- * File Operations
- *
- * Open
- *
- * Creates a new cache map file if one doesn't exist.
- * Returns error if it detects change in format or cache wasn't closed.
- */
- nsresult Open( nsIFile * cacheDirectory,
- nsDiskCache::CorruptCacheInfo * corruptInfo);
- nsresult Close(bool flush);
- nsresult Trim();
- nsresult FlushHeader();
- nsresult FlushRecords( bool unswap);
- void NotifyCapacityChange(uint32_t capacity);
- /**
- * Record operations
- */
- nsresult AddRecord( nsDiskCacheRecord * mapRecord, nsDiskCacheRecord * oldRecord);
- nsresult UpdateRecord( nsDiskCacheRecord * mapRecord);
- nsresult FindRecord( uint32_t hashNumber, nsDiskCacheRecord * mapRecord);
- nsresult DeleteRecord( nsDiskCacheRecord * mapRecord);
- nsresult VisitRecords( nsDiskCacheRecordVisitor * visitor);
- nsresult EvictRecords( nsDiskCacheRecordVisitor * visitor);
- /**
- * Disk Entry operations
- */
- nsresult DeleteStorage( nsDiskCacheRecord * record);
- nsresult GetFileForDiskCacheRecord( nsDiskCacheRecord * record,
- bool meta,
- bool createPath,
- nsIFile ** result);
-
- nsresult GetLocalFileForDiskCacheRecord( nsDiskCacheRecord * record,
- bool meta,
- bool createPath,
- nsIFile ** result);
- // On success, this returns the buffer owned by nsDiskCacheMap,
- // so it must not be deleted by the caller.
- nsDiskCacheEntry * ReadDiskCacheEntry( nsDiskCacheRecord * record);
- nsresult WriteDiskCacheEntry( nsDiskCacheBinding * binding);
-
- nsresult ReadDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
- nsresult WriteDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, uint32_t size);
- nsresult DeleteStorage( nsDiskCacheRecord * record, bool metaData);
-
- /**
- * Statistical Operations
- */
- void IncrementTotalSize( uint32_t delta)
- {
- mHeader.mDataSize += delta;
- mHeader.mIsDirty = true;
- }
-
- void DecrementTotalSize( uint32_t delta)
- {
- NS_ASSERTION(mHeader.mDataSize >= delta, "disk cache size negative?");
- mHeader.mDataSize = mHeader.mDataSize > delta ? mHeader.mDataSize - delta : 0;
- mHeader.mIsDirty = true;
- }
-
- inline void IncrementTotalSize( uint32_t blocks, uint32_t blockSize)
- {
- // Round up to nearest K
- IncrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
- }
- inline void DecrementTotalSize( uint32_t blocks, uint32_t blockSize)
- {
- // Round up to nearest K
- DecrementTotalSize(((blocks*blockSize) + 0x03FF) >> 10);
- }
-
- uint32_t TotalSize() { return mHeader.mDataSize; }
-
- int32_t EntryCount() { return mHeader.mEntryCount; }
- size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
- private:
- /**
- * Private methods
- */
- nsresult OpenBlockFiles(nsDiskCache::CorruptCacheInfo * corruptInfo);
- nsresult CloseBlockFiles(bool flush);
- bool CacheFilesExist();
- nsresult CreateCacheSubDirectories();
- uint32_t CalculateFileIndex(uint32_t size);
- nsresult GetBlockFileForIndex( uint32_t index, nsIFile ** result);
- uint32_t GetBlockSizeForIndex( uint32_t index) const {
- return BLOCK_SIZE_FOR_INDEX(index);
- }
- uint32_t GetBitMapSizeForIndex( uint32_t index) const {
- return BITMAP_SIZE_FOR_INDEX(index);
- }
-
- // returns the bucket number
- uint32_t GetBucketIndex( uint32_t hashNumber) const {
- return (hashNumber & (kBuckets - 1));
- }
-
- // Gets the size of the bucket (in number of records)
- uint32_t GetRecordsPerBucket() const {
- return mHeader.mRecordCount / kBuckets;
- }
- // Gets the first record in the bucket
- nsDiskCacheRecord *GetFirstRecordInBucket(uint32_t bucket) const {
- return mRecordArray + bucket * GetRecordsPerBucket();
- }
- uint32_t GetBucketRank(uint32_t bucketIndex, uint32_t targetRank);
- int32_t VisitEachRecord(uint32_t bucketIndex,
- nsDiskCacheRecordVisitor * visitor,
- uint32_t evictionRank);
- nsresult GrowRecords();
- nsresult ShrinkRecords();
- nsresult EnsureBuffer(uint32_t bufSize);
- // The returned structure will point to the buffer owned by nsDiskCacheMap,
- // so it must not be deleted by the caller.
- nsDiskCacheEntry * CreateDiskCacheEntry(nsDiskCacheBinding * binding,
- uint32_t * size);
- // Initializes the _CACHE_CLEAN_ related functionality
- nsresult InitCacheClean(nsIFile * cacheDirectory,
- nsDiskCache::CorruptCacheInfo * corruptInfo);
- // Writes out a value of '0' or '1' in the _CACHE_CLEAN_ file
- nsresult WriteCacheClean(bool clean);
- // Resets the timout for revalidating the cache
- nsresult ResetCacheTimer(int32_t timeout = kRevalidateCacheTimeout);
- // Invalidates the cache, calls WriteCacheClean and ResetCacheTimer
- nsresult InvalidateCache();
- // Determines if the cache is in a safe state
- bool IsCacheInSafeState();
- // Revalidates the cache by writting out the header, records, and finally
- // by calling WriteCacheClean(true).
- nsresult RevalidateCache();
- // Timer which revalidates the cache
- static void RevalidateTimerCallback(nsITimer *aTimer, void *arg);
- /**
- * data members
- */
- private:
- nsCOMPtr<nsITimer> mCleanCacheTimer;
- nsCOMPtr<nsIFile> mCacheDirectory;
- PRFileDesc * mMapFD;
- PRFileDesc * mCleanFD;
- nsDiskCacheRecord * mRecordArray;
- nsDiskCacheBlockFile mBlockFile[kNumBlockFiles];
- uint32_t mBufferSize;
- char * mBuffer;
- nsDiskCacheHeader mHeader;
- int32_t mMaxRecordCount;
- bool mIsDirtyCacheFlushed;
- PRIntervalTime mLastInvalidateTime;
- };
- #endif // _nsDiskCacheMap_h_
|