Back to index

lightning-sunbird  0.9+nobinonly
nsDiskCacheMap.h
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:set ts=4 sw=4 sts=4 cin et: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is nsDiskCacheMap.h, released
00017  * March 23, 2001.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 2001
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Patrick C. Beard <beard@netscape.com>
00026  *   Gordon Sheridan  <gordon@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #ifndef _nsDiskCacheMap_h_
00043 #define _nsDiskCacheMap_h_
00044 
00045 #include <limits.h>
00046 
00047 #include "prtypes.h"
00048 #include "prnetdb.h"
00049 #include "nsDebug.h"
00050 #include "nsError.h"
00051 #include "nsILocalFile.h"
00052 
00053 #include "nsDiskCache.h"
00054 #include "nsDiskCacheBlockFile.h"
00055  
00056  
00057 class nsDiskCacheBinding;
00058 struct nsDiskCacheEntry;
00059 
00060 /******************************************************************************
00061  *  nsDiskCacheRecord
00062  *
00063  *   Cache Location Format
00064  *
00065  *    1000 0000 0000 0000 0000 0000 0000 0000 : initialized bit
00066  *
00067  *    0011 0000 0000 0000 0000 0000 0000 0000 : File Selector (0 = separate file)
00068  *    0000 0011 0000 0000 0000 0000 0000 0000 : number of extra contiguous blocks 1-4
00069  *    0100 1100 0000 0000 0000 0000 0000 0000 : reserved bits
00070  *    0000 0000 1111 1111 1111 1111 1111 1111 : block#  0-16777216 (2^24)
00071  *
00072  *    0000 0000 1111 1111 1111 1111 0000 0000 : eFileSizeMask (size of file in k: see note)
00073  *    0000 0000 0000 0000 0000 0000 1111 1111 : eFileGenerationMask
00074  *
00075  *  File Selector:
00076  *      0 = separate file on disk
00077  *      1 = 256 byte block file
00078  *      2 = 1k block file
00079  *      3 = 4k block file
00080  *
00081  *  eFileSizeMask note:  Files larger than 64 MiB have zero size stored in the
00082  *                       location.  The file itself must be examined to determine
00083  *                       its actual size.  (XXX This is broken in places -darin)
00084  *
00085  *****************************************************************************/
00086 
00087 #define BLOCK_SIZE_FOR_INDEX(index)  ((index) ? (256 << (2 * ((index) - 1))) : 0)
00088 
00089 // Min and max values for the number of records in the DiskCachemap
00090 #define kMinRecordCount    512
00091 #define kMaxRecordCount    8192
00092 
00093 #define kSeparateFile      0
00094 #define kMaxDataFileSize   0x4000000   // 64 MiB
00095 #define kBuckets           (1 << 5)    // must be a power of 2!
00096 
00097 class nsDiskCacheRecord {
00098 
00099 private:
00100     PRUint32    mHashNumber;
00101     PRUint32    mEvictionRank;
00102     PRUint32    mDataLocation;
00103     PRUint32    mMetaLocation;
00104  
00105     enum {
00106         eLocationInitializedMask = 0x80000000,
00107         
00108         eLocationSelectorMask    = 0x30000000,
00109         eLocationSelectorOffset  = 28,
00110         
00111         eExtraBlocksMask         = 0x03000000,
00112         eExtraBlocksOffset       = 24,
00113         
00114         eReservedMask            = 0x4C000000,
00115         
00116         eBlockNumberMask         = 0x00FFFFFF,
00117 
00118         eFileSizeMask            = 0x00FFFF00,
00119         eFileSizeOffset          = 8,
00120         eFileGenerationMask      = 0x000000FF,
00121         eFileReservedMask        = 0x4F000000
00122         
00123     };
00124 
00125 public:
00126     nsDiskCacheRecord()
00127         :   mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
00128     {
00129     }
00130     
00131     PRBool  ValidRecord()
00132     {
00133         if ((mDataLocation & eReservedMask) || (mMetaLocation & eReservedMask))
00134             return PR_FALSE;
00135         return PR_TRUE;
00136     }
00137     
00138     // HashNumber accessors
00139     PRUint32  HashNumber() const                  { return mHashNumber; }
00140     void      SetHashNumber( PRUint32 hashNumber) { mHashNumber = hashNumber; }
00141 
00142     // EvictionRank accessors
00143     PRUint32  EvictionRank() const              { return mEvictionRank; }
00144     void      SetEvictionRank( PRUint32 rank)   { mEvictionRank = rank ? rank : 1; }
00145 
00146     // DataLocation accessors
00147     PRBool    DataLocationInitialized() const { return mDataLocation & eLocationInitializedMask; }
00148     void      ClearDataLocation()       { mDataLocation = 0; }
00149     
00150     PRUint32  DataFile() const
00151     {
00152         return (PRUint32)(mDataLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
00153     }
00154 
00155     void      SetDataBlocks( PRUint32 index, PRUint32 startBlock, PRUint32 blockCount)
00156     {
00157         // clear everything
00158         mDataLocation = 0;
00159         
00160         // set file index
00161         NS_ASSERTION( index < 4,"invalid location index");
00162         NS_ASSERTION( index > 0,"invalid location index");
00163         mDataLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
00164 
00165         // set startBlock
00166         NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
00167         mDataLocation |= startBlock & eBlockNumberMask;
00168         
00169         // set blockCount
00170         NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
00171         blockCount = --blockCount;
00172         mDataLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
00173         
00174         mDataLocation |= eLocationInitializedMask;
00175     }
00176 
00177     PRUint32   DataBlockCount() const
00178     {
00179         return (PRUint32)((mDataLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
00180     }
00181 
00182     PRUint32   DataStartBlock() const
00183     {
00184         return (mDataLocation & eBlockNumberMask);
00185     }
00186     
00187     PRUint32   DataBlockSize() const
00188     {
00189         return BLOCK_SIZE_FOR_INDEX(DataFile());
00190     }
00191     
00192     PRUint32   DataFileSize() const  { return (mDataLocation & eFileSizeMask) >> eFileSizeOffset; }
00193     void       SetDataFileSize(PRUint32  size)
00194     {
00195         NS_ASSERTION((mDataLocation & eFileReservedMask) == 0, "bad location");
00196         mDataLocation &= ~eFileSizeMask;    // clear eFileSizeMask
00197         mDataLocation |= (size << eFileSizeOffset) & eFileSizeMask;
00198     }
00199 
00200     PRUint8   DataFileGeneration() const
00201     {
00202         return (mDataLocation & eFileGenerationMask);
00203     }
00204 
00205     void       SetDataFileGeneration( PRUint8 generation)
00206     {
00207         // clear everything, (separate file index = 0)
00208         mDataLocation = 0;
00209         mDataLocation |= generation & eFileGenerationMask;
00210         mDataLocation |= eLocationInitializedMask;
00211     }
00212 
00213     // MetaLocation accessors
00214     PRBool    MetaLocationInitialized() const { return mMetaLocation & eLocationInitializedMask; }
00215     void      ClearMetaLocation()             { mMetaLocation = 0; }   
00216     PRUint32  MetaLocation() const            { return mMetaLocation; }
00217     
00218     PRUint32  MetaFile() const
00219     {
00220         return (PRUint32)(mMetaLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
00221     }
00222 
00223     void      SetMetaBlocks( PRUint32 index, PRUint32 startBlock, PRUint32 blockCount)
00224     {
00225         // clear everything
00226         mMetaLocation = 0;
00227         
00228         // set file index
00229         NS_ASSERTION( index < 4, "invalid location index");
00230         NS_ASSERTION( index > 0, "invalid location index");
00231         mMetaLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
00232 
00233         // set startBlock
00234         NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
00235         mMetaLocation |= startBlock & eBlockNumberMask;
00236         
00237         // set blockCount
00238         NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
00239         blockCount = --blockCount;
00240         mMetaLocation |= (blockCount << eExtraBlocksOffset) & eExtraBlocksMask;
00241         
00242         mMetaLocation |= eLocationInitializedMask;
00243     }
00244 
00245     PRUint32   MetaBlockCount() const
00246     {
00247         return (PRUint32)((mMetaLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
00248     }
00249 
00250     PRUint32   MetaStartBlock() const
00251     {
00252         return (mMetaLocation & eBlockNumberMask);
00253     }
00254 
00255     PRUint32   MetaBlockSize() const
00256     {
00257         return BLOCK_SIZE_FOR_INDEX(MetaFile());
00258     }
00259     
00260     PRUint32   MetaFileSize() const  { return (mMetaLocation & eFileSizeMask) >> eFileSizeOffset; }
00261     void       SetMetaFileSize(PRUint32  size)
00262     {
00263         mMetaLocation &= ~eFileSizeMask;    // clear eFileSizeMask
00264         mMetaLocation |= (size << eFileSizeOffset) & eFileSizeMask;
00265     }
00266 
00267     PRUint8   MetaFileGeneration() const
00268     {
00269         return (mMetaLocation & eFileGenerationMask);
00270     }
00271 
00272     void       SetMetaFileGeneration( PRUint8 generation)
00273     {
00274         // clear everything, (separate file index = 0)
00275         mMetaLocation = 0;
00276         mMetaLocation |= generation & eFileGenerationMask;
00277         mMetaLocation |= eLocationInitializedMask;
00278     }
00279 
00280     PRUint8   Generation() const
00281     {
00282         if ((mDataLocation & eLocationInitializedMask)  &&
00283             (DataFile() == 0))
00284             return DataFileGeneration();
00285             
00286         if ((mMetaLocation & eLocationInitializedMask)  &&
00287             (MetaFile() == 0))
00288             return MetaFileGeneration();
00289         
00290         return 0;  // no generation
00291     }
00292 
00293 #if defined(IS_LITTLE_ENDIAN)
00294     void        Swap()
00295     {
00296         mHashNumber   = ::PR_htonl(mHashNumber);
00297         mEvictionRank = ::PR_htonl(mEvictionRank);
00298         mDataLocation = ::PR_htonl(mDataLocation);
00299         mMetaLocation = ::PR_htonl(mMetaLocation);
00300     }
00301 #endif
00302     
00303 #if defined(IS_LITTLE_ENDIAN)
00304     void        Unswap()
00305     {
00306         mHashNumber   = ::PR_ntohl(mHashNumber);
00307         mEvictionRank = ::PR_ntohl(mEvictionRank);
00308         mDataLocation = ::PR_ntohl(mDataLocation);
00309         mMetaLocation = ::PR_ntohl(mMetaLocation);
00310     }
00311 #endif
00312 
00313 };
00314 
00315 
00316 /******************************************************************************
00317  *  nsDiskCacheRecordVisitor
00318  *****************************************************************************/
00319 
00320 enum {  kDeleteRecordAndContinue = -1,
00321         kStopVisitingRecords     =  0,
00322         kVisitNextRecord         =  1
00323 };
00324 
00325 class nsDiskCacheRecordVisitor {
00326     public:
00327 
00328     virtual PRInt32  VisitRecord( nsDiskCacheRecord *  mapRecord) = 0;
00329 };
00330 
00331 
00332 /******************************************************************************
00333  *  nsDiskCacheHeader
00334  *****************************************************************************/
00335 
00336 struct nsDiskCacheHeader {
00337     PRUint32    mVersion;                           // cache version.
00338     PRInt32     mDataSize;                          // size of cache in bytes.
00339     PRInt32     mEntryCount;                        // number of entries stored in cache.
00340     PRUint32    mIsDirty;                           // dirty flag.
00341     PRInt32     mRecordCount;                       // Number of records
00342     PRUint32    mEvictionRank[kBuckets];            // Highest EvictionRank of the bucket
00343     PRUint32    mBucketUsage[kBuckets];             // Number of used entries in the bucket
00344   
00345     nsDiskCacheHeader()
00346         : mVersion(nsDiskCache::kCurrentVersion)
00347         , mDataSize(0)
00348         , mEntryCount(0)
00349         , mIsDirty(PR_TRUE)
00350         , mRecordCount(0)
00351     {}
00352 
00353     void        Swap()
00354     {
00355 #if defined(IS_LITTLE_ENDIAN)
00356         mVersion     = ::PR_htonl(mVersion);
00357         mDataSize    = ::PR_htonl(mDataSize);
00358         mEntryCount  = ::PR_htonl(mEntryCount);
00359         mIsDirty     = ::PR_htonl(mIsDirty);
00360         mRecordCount = ::PR_htonl(mRecordCount);
00361 
00362         for (PRUint32 i = 0; i < kBuckets ; i++) {
00363             mEvictionRank[i] = ::PR_htonl(mEvictionRank[i]);
00364             mBucketUsage[i]  = ::PR_htonl(mBucketUsage[i]);
00365         }
00366 #endif
00367     }
00368     
00369     void        Unswap()
00370     {
00371 #if defined(IS_LITTLE_ENDIAN)
00372         mVersion     = ::PR_ntohl(mVersion);
00373         mDataSize    = ::PR_ntohl(mDataSize);
00374         mEntryCount  = ::PR_ntohl(mEntryCount);
00375         mIsDirty     = ::PR_ntohl(mIsDirty);
00376         mRecordCount = ::PR_ntohl(mRecordCount);
00377 
00378         for (PRUint32 i = 0; i < kBuckets ; i++) {
00379             mEvictionRank[i] = ::PR_ntohl(mEvictionRank[i]);
00380             mBucketUsage[i]  = ::PR_ntohl(mBucketUsage[i]);
00381         }
00382 #endif
00383     }
00384 };
00385 
00386 
00387 /******************************************************************************
00388  *  nsDiskCacheMap
00389  *****************************************************************************/
00390 
00391 class nsDiskCacheMap {
00392 public:
00393 
00394      nsDiskCacheMap() : mCacheDirectory(nsnull), mMapFD(nsnull), mRecordArray(nsnull) { }
00395     ~nsDiskCacheMap() { (void) Close(PR_TRUE); }
00396 
00405     nsresult  Open( nsILocalFile *  cacheDirectory);
00406     nsresult  Close(PRBool flush);
00407     nsresult  Trim();
00408 
00409     nsresult  FlushHeader();
00410     nsresult  FlushRecords( PRBool unswap);
00411 
00415     nsresult AddRecord( nsDiskCacheRecord *  mapRecord, nsDiskCacheRecord * oldRecord);
00416     nsresult UpdateRecord( nsDiskCacheRecord *  mapRecord);
00417     nsresult FindRecord( PRUint32  hashNumber, nsDiskCacheRecord *  mapRecord);
00418     nsresult DeleteRecord( nsDiskCacheRecord *  mapRecord);
00419     nsresult VisitRecords( nsDiskCacheRecordVisitor * visitor);
00420     nsresult EvictRecords( nsDiskCacheRecordVisitor * visitor);
00421 
00425     nsresult    DoomRecord( nsDiskCacheRecord *  record);
00426     nsresult    DeleteStorage( nsDiskCacheRecord *  record);
00427     nsresult    DeleteRecordAndStorage( nsDiskCacheRecord *  record);
00428 
00429     nsresult    GetFileForDiskCacheRecord( nsDiskCacheRecord * record,
00430                                            PRBool              meta,
00431                                            nsIFile **          result);
00432                                           
00433     nsresult    GetLocalFileForDiskCacheRecord( nsDiskCacheRecord *  record,
00434                                                 PRBool               meta,
00435                                                 nsILocalFile **      result);
00436 
00437     nsresult    ReadDiskCacheEntry( nsDiskCacheRecord *  record,
00438                                     nsDiskCacheEntry **  result);
00439 
00440     nsresult    WriteDiskCacheEntry( nsDiskCacheBinding *  binding);
00441     
00442     nsresult    ReadDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, PRUint32 size);
00443     nsresult    WriteDataCacheBlocks(nsDiskCacheBinding * binding, char * buffer, PRUint32 size);
00444     nsresult    DeleteStorage( nsDiskCacheRecord * record, PRBool metaData);
00445     
00449     void     IncrementTotalSize( PRInt32  delta)
00450              {
00451                 NS_ASSERTION(mHeader.mDataSize >= 0, "disk cache size negative?");
00452                 mHeader.mDataSize += delta;
00453                 mHeader.mIsDirty   = PR_TRUE;
00454              }
00455              
00456     void     DecrementTotalSize( PRInt32  delta)
00457              {
00458                 mHeader.mDataSize -= delta;
00459                 mHeader.mIsDirty   = PR_TRUE;
00460                 NS_ASSERTION(mHeader.mDataSize >= 0, "disk cache size negative?");
00461              }
00462     
00463     PRInt32  TotalSize()   { return mHeader.mDataSize; }
00464     
00465     PRInt32  EntryCount()  { return mHeader.mEntryCount; }
00466 
00467 
00468 private:
00469 
00473     nsresult    OpenBlockFiles();
00474     nsresult    CloseBlockFiles(PRBool flush);
00475     PRBool      CacheFilesExist();
00476 
00477     PRUint32    CalculateFileIndex(PRUint32 size);
00478 
00479     nsresult    GetBlockFileForIndex( PRUint32 index, nsILocalFile ** result);
00480     PRUint32    GetBlockSizeForIndex( PRUint32 index) const {
00481         return BLOCK_SIZE_FOR_INDEX(index);
00482     }
00483     
00484     // returns the bucket number    
00485     PRUint32 GetBucketIndex( PRUint32 hashNumber) const {
00486         return (hashNumber & (kBuckets - 1));
00487     }
00488     
00489     // Gets the size of the bucket (in number of records)
00490     PRUint32 GetRecordsPerBucket() const {
00491         return mHeader.mRecordCount / kBuckets;
00492     }
00493 
00494     // Gets the first record in the bucket
00495     nsDiskCacheRecord *GetFirstRecordInBucket(PRUint32 bucket) const {
00496         return mRecordArray + bucket * GetRecordsPerBucket();
00497     }
00498 
00499     PRUint32 GetBucketRank(PRUint32 bucketIndex, PRUint32 targetRank);
00500 
00501     PRInt32  VisitEachRecord(PRUint32                    bucketIndex,
00502                              nsDiskCacheRecordVisitor *  visitor,
00503                              PRUint32                    evictionRank);
00504 
00505     nsresult GrowRecords();
00506     nsresult ShrinkRecords();
00507 
00511 private:
00512     nsCOMPtr<nsILocalFile>  mCacheDirectory;
00513     PRFileDesc *            mMapFD;
00514     nsDiskCacheRecord *     mRecordArray;
00515     nsDiskCacheBlockFile    mBlockFile[3];
00516     nsDiskCacheHeader       mHeader;
00517 };
00518 
00519 #endif // _nsDiskCacheMap_h_