summaryrefslogtreecommitdiffstats
path: root/netwerk/cache/nsDiskCacheMap.h
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/cache/nsDiskCacheMap.h')
-rw-r--r--netwerk/cache/nsDiskCacheMap.h577
1 files changed, 577 insertions, 0 deletions
diff --git a/netwerk/cache/nsDiskCacheMap.h b/netwerk/cache/nsDiskCacheMap.h
new file mode 100644
index 000000000..77af2586f
--- /dev/null
+++ b/netwerk/cache/nsDiskCacheMap.h
@@ -0,0 +1,577 @@
+/* -*- 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_