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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is nsDiskCacheMap.h, released
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2001
22 * the Initial Developer. All Rights Reserved.
25 * Patrick C. Beard <beard@netscape.com>
26 * Gordon Sheridan <gordon@netscape.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #ifndef _nsDiskCacheMap_h_
43 #define _nsDiskCacheMap_h_
51 #include "nsILocalFile.h"
53 #include "nsDiskCache.h"
54 #include "nsDiskCacheBlockFile.h"
57 class nsDiskCacheBinding
;
58 struct nsDiskCacheEntry
;
60 /******************************************************************************
63 * Cache Location Format
65 * 1000 0000 0000 0000 0000 0000 0000 0000 : initialized bit
67 * 0011 0000 0000 0000 0000 0000 0000 0000 : File Selector (0 = separate file)
68 * 0000 0011 0000 0000 0000 0000 0000 0000 : number of extra contiguous blocks 1-4
69 * 0100 1100 0000 0000 0000 0000 0000 0000 : reserved bits
70 * 0000 0000 1111 1111 1111 1111 1111 1111 : block# 0-16777216 (2^24)
72 * 0000 0000 1111 1111 1111 1111 0000 0000 : eFileSizeMask (size of file in k: see note)
73 * 0000 0000 0000 0000 0000 0000 1111 1111 : eFileGenerationMask
76 * 0 = separate file on disk
77 * 1 = 256 byte block file
81 * eFileSizeMask note: Files larger than 64 MiB have zero size stored in the
82 * location. The file itself must be examined to determine
83 * its actual size. (XXX This is broken in places -darin)
85 *****************************************************************************/
87 #define BLOCK_SIZE_FOR_INDEX(index) ((index) ? (256 << (2 * ((index) - 1))) : 0)
89 // Min and max values for the number of records in the DiskCachemap
90 #define kMinRecordCount 512
92 #define kSeparateFile 0
93 // #define must always be <= 65535KB, or overflow. See bug 443067 Comment 8
94 #define kMaxDataFileSize 5 * 1024 * 1024 // 5 MB (in bytes)
95 #define kBuckets (1 << 5) // must be a power of 2!
97 class nsDiskCacheRecord
{
100 PRUint32 mHashNumber
;
101 PRUint32 mEvictionRank
;
102 PRUint32 mDataLocation
;
103 PRUint32 mMetaLocation
;
106 eLocationInitializedMask
= 0x80000000,
108 eLocationSelectorMask
= 0x30000000,
109 eLocationSelectorOffset
= 28,
111 eExtraBlocksMask
= 0x03000000,
112 eExtraBlocksOffset
= 24,
114 eReservedMask
= 0x4C000000,
116 eBlockNumberMask
= 0x00FFFFFF,
118 eFileSizeMask
= 0x00FFFF00,
120 eFileGenerationMask
= 0x000000FF,
121 eFileReservedMask
= 0x4F000000
127 : mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
133 if ((mDataLocation
& eReservedMask
) || (mMetaLocation
& eReservedMask
))
138 // HashNumber accessors
139 PRUint32
HashNumber() const { return mHashNumber
; }
140 void SetHashNumber( PRUint32 hashNumber
) { mHashNumber
= hashNumber
; }
142 // EvictionRank accessors
143 PRUint32
EvictionRank() const { return mEvictionRank
; }
144 void SetEvictionRank( PRUint32 rank
) { mEvictionRank
= rank
? rank
: 1; }
146 // DataLocation accessors
147 PRBool
DataLocationInitialized() const { return 0 != (mDataLocation
& eLocationInitializedMask
); }
148 void ClearDataLocation() { mDataLocation
= 0; }
150 PRUint32
DataFile() const
152 return (PRUint32
)(mDataLocation
& eLocationSelectorMask
) >> eLocationSelectorOffset
;
155 void SetDataBlocks( PRUint32 index
, PRUint32 startBlock
, PRUint32 blockCount
)
161 NS_ASSERTION( index
< 4,"invalid location index");
162 NS_ASSERTION( index
> 0,"invalid location index");
163 mDataLocation
|= (index
<< eLocationSelectorOffset
) & eLocationSelectorMask
;
166 NS_ASSERTION(startBlock
== (startBlock
& eBlockNumberMask
), "invalid block number");
167 mDataLocation
|= startBlock
& eBlockNumberMask
;
170 NS_ASSERTION( (blockCount
>=1) && (blockCount
<=4),"invalid block count");
172 mDataLocation
|= (blockCount
<< eExtraBlocksOffset
) & eExtraBlocksMask
;
174 mDataLocation
|= eLocationInitializedMask
;
177 PRUint32
DataBlockCount() const
179 return (PRUint32
)((mDataLocation
& eExtraBlocksMask
) >> eExtraBlocksOffset
) + 1;
182 PRUint32
DataStartBlock() const
184 return (mDataLocation
& eBlockNumberMask
);
187 PRUint32
DataBlockSize() const
189 return BLOCK_SIZE_FOR_INDEX(DataFile());
192 PRUint32
DataFileSize() const { return (mDataLocation
& eFileSizeMask
) >> eFileSizeOffset
; }
193 void SetDataFileSize(PRUint32 size
)
195 NS_ASSERTION((mDataLocation
& eFileReservedMask
) == 0, "bad location");
196 mDataLocation
&= ~eFileSizeMask
; // clear eFileSizeMask
197 mDataLocation
|= (size
<< eFileSizeOffset
) & eFileSizeMask
;
200 PRUint8
DataFileGeneration() const
202 return (mDataLocation
& eFileGenerationMask
);
205 void SetDataFileGeneration( PRUint8 generation
)
207 // clear everything, (separate file index = 0)
209 mDataLocation
|= generation
& eFileGenerationMask
;
210 mDataLocation
|= eLocationInitializedMask
;
213 // MetaLocation accessors
214 PRBool
MetaLocationInitialized() const { return 0 != (mMetaLocation
& eLocationInitializedMask
); }
215 void ClearMetaLocation() { mMetaLocation
= 0; }
216 PRUint32
MetaLocation() const { return mMetaLocation
; }
218 PRUint32
MetaFile() const
220 return (PRUint32
)(mMetaLocation
& eLocationSelectorMask
) >> eLocationSelectorOffset
;
223 void SetMetaBlocks( PRUint32 index
, PRUint32 startBlock
, PRUint32 blockCount
)
229 NS_ASSERTION( index
< 4, "invalid location index");
230 NS_ASSERTION( index
> 0, "invalid location index");
231 mMetaLocation
|= (index
<< eLocationSelectorOffset
) & eLocationSelectorMask
;
234 NS_ASSERTION(startBlock
== (startBlock
& eBlockNumberMask
), "invalid block number");
235 mMetaLocation
|= startBlock
& eBlockNumberMask
;
238 NS_ASSERTION( (blockCount
>=1) && (blockCount
<=4),"invalid block count");
240 mMetaLocation
|= (blockCount
<< eExtraBlocksOffset
) & eExtraBlocksMask
;
242 mMetaLocation
|= eLocationInitializedMask
;
245 PRUint32
MetaBlockCount() const
247 return (PRUint32
)((mMetaLocation
& eExtraBlocksMask
) >> eExtraBlocksOffset
) + 1;
250 PRUint32
MetaStartBlock() const
252 return (mMetaLocation
& eBlockNumberMask
);
255 PRUint32
MetaBlockSize() const
257 return BLOCK_SIZE_FOR_INDEX(MetaFile());
260 PRUint32
MetaFileSize() const { return (mMetaLocation
& eFileSizeMask
) >> eFileSizeOffset
; }
261 void SetMetaFileSize(PRUint32 size
)
263 mMetaLocation
&= ~eFileSizeMask
; // clear eFileSizeMask
264 mMetaLocation
|= (size
<< eFileSizeOffset
) & eFileSizeMask
;
267 PRUint8
MetaFileGeneration() const
269 return (mMetaLocation
& eFileGenerationMask
);
272 void SetMetaFileGeneration( PRUint8 generation
)
274 // clear everything, (separate file index = 0)
276 mMetaLocation
|= generation
& eFileGenerationMask
;
277 mMetaLocation
|= eLocationInitializedMask
;
280 PRUint8
Generation() const
282 if ((mDataLocation
& eLocationInitializedMask
) &&
284 return DataFileGeneration();
286 if ((mMetaLocation
& eLocationInitializedMask
) &&
288 return MetaFileGeneration();
290 return 0; // no generation
293 #if defined(IS_LITTLE_ENDIAN)
296 mHashNumber
= htonl(mHashNumber
);
297 mEvictionRank
= htonl(mEvictionRank
);
298 mDataLocation
= htonl(mDataLocation
);
299 mMetaLocation
= htonl(mMetaLocation
);
303 #if defined(IS_LITTLE_ENDIAN)
306 mHashNumber
= ntohl(mHashNumber
);
307 mEvictionRank
= ntohl(mEvictionRank
);
308 mDataLocation
= ntohl(mDataLocation
);
309 mMetaLocation
= ntohl(mMetaLocation
);
316 /******************************************************************************
317 * nsDiskCacheRecordVisitor
318 *****************************************************************************/
320 enum { kDeleteRecordAndContinue
= -1,
321 kStopVisitingRecords
= 0,
325 class nsDiskCacheRecordVisitor
{
328 virtual PRInt32
VisitRecord( nsDiskCacheRecord
* mapRecord
) = 0;
332 /******************************************************************************
334 *****************************************************************************/
336 struct nsDiskCacheHeader
{
337 PRUint32 mVersion
; // cache version.
338 PRUint32 mDataSize
; // size of cache in units of 1024bytes.
339 PRInt32 mEntryCount
; // number of entries stored in cache.
340 PRUint32 mIsDirty
; // dirty flag.
341 PRInt32 mRecordCount
; // Number of records
342 PRUint32 mEvictionRank
[kBuckets
]; // Highest EvictionRank of the bucket
343 PRUint32 mBucketUsage
[kBuckets
]; // Number of used entries in the bucket
346 : mVersion(nsDiskCache::kCurrentVersion
)
355 #if defined(IS_LITTLE_ENDIAN)
356 mVersion
= htonl(mVersion
);
357 mDataSize
= htonl(mDataSize
);
358 mEntryCount
= htonl(mEntryCount
);
359 mIsDirty
= htonl(mIsDirty
);
360 mRecordCount
= htonl(mRecordCount
);
362 for (PRUint32 i
= 0; i
< kBuckets
; i
++) {
363 mEvictionRank
[i
] = htonl(mEvictionRank
[i
]);
364 mBucketUsage
[i
] = htonl(mBucketUsage
[i
]);
371 #if defined(IS_LITTLE_ENDIAN)
372 mVersion
= ntohl(mVersion
);
373 mDataSize
= ntohl(mDataSize
);
374 mEntryCount
= ntohl(mEntryCount
);
375 mIsDirty
= ntohl(mIsDirty
);
376 mRecordCount
= ntohl(mRecordCount
);
378 for (PRUint32 i
= 0; i
< kBuckets
; i
++) {
379 mEvictionRank
[i
] = ntohl(mEvictionRank
[i
]);
380 mBucketUsage
[i
] = ntohl(mBucketUsage
[i
]);
387 /******************************************************************************
389 *****************************************************************************/
391 class nsDiskCacheMap
{
395 mCacheDirectory(nsnull
),
397 mRecordArray(nsnull
),
400 mMaxRecordCount(16384) // this default value won't matter
404 (void) Close(PR_TRUE
);
412 * Creates a new cache map file if one doesn't exist.
413 * Returns error if it detects change in format or cache wasn't closed.
415 nsresult
Open( nsILocalFile
* cacheDirectory
);
416 nsresult
Close(PRBool flush
);
419 nsresult
FlushHeader();
420 nsresult
FlushRecords( PRBool unswap
);
422 void NotifyCapacityChange(PRUint32 capacity
);
427 nsresult
AddRecord( nsDiskCacheRecord
* mapRecord
, nsDiskCacheRecord
* oldRecord
);
428 nsresult
UpdateRecord( nsDiskCacheRecord
* mapRecord
);
429 nsresult
FindRecord( PRUint32 hashNumber
, nsDiskCacheRecord
* mapRecord
);
430 nsresult
DeleteRecord( nsDiskCacheRecord
* mapRecord
);
431 nsresult
VisitRecords( nsDiskCacheRecordVisitor
* visitor
);
432 nsresult
EvictRecords( nsDiskCacheRecordVisitor
* visitor
);
435 * Disk Entry operations
437 nsresult
DeleteStorage( nsDiskCacheRecord
* record
);
439 nsresult
GetFileForDiskCacheRecord( nsDiskCacheRecord
* record
,
443 nsresult
GetLocalFileForDiskCacheRecord( nsDiskCacheRecord
* record
,
445 nsILocalFile
** result
);
447 // On success, this returns the buffer owned by nsDiskCacheMap,
448 // so it must not be deleted by the caller.
449 nsDiskCacheEntry
* ReadDiskCacheEntry( nsDiskCacheRecord
* record
);
451 nsresult
WriteDiskCacheEntry( nsDiskCacheBinding
* binding
);
453 nsresult
ReadDataCacheBlocks(nsDiskCacheBinding
* binding
, char * buffer
, PRUint32 size
);
454 nsresult
WriteDataCacheBlocks(nsDiskCacheBinding
* binding
, char * buffer
, PRUint32 size
);
455 nsresult
DeleteStorage( nsDiskCacheRecord
* record
, PRBool metaData
);
458 * Statistical Operations
460 void IncrementTotalSize( PRUint32 delta
)
462 mHeader
.mDataSize
+= delta
;
463 mHeader
.mIsDirty
= PR_TRUE
;
466 void DecrementTotalSize( PRUint32 delta
)
468 NS_ASSERTION(mHeader
.mDataSize
>= delta
, "disk cache size negative?");
469 mHeader
.mDataSize
= mHeader
.mDataSize
> delta
? mHeader
.mDataSize
- delta
: 0;
470 mHeader
.mIsDirty
= PR_TRUE
;
473 inline void IncrementTotalSize( PRUint32 blocks
, PRUint32 blockSize
)
475 // Round up to nearest K
476 IncrementTotalSize(((blocks
*blockSize
) + 0x03FF) >> 10);
479 inline void DecrementTotalSize( PRUint32 blocks
, PRUint32 blockSize
)
481 // Round up to nearest K
482 DecrementTotalSize(((blocks
*blockSize
) + 0x03FF) >> 10);
485 PRUint32
TotalSize() { return mHeader
.mDataSize
; }
487 PRInt32
EntryCount() { return mHeader
.mEntryCount
; }
495 nsresult
OpenBlockFiles();
496 nsresult
CloseBlockFiles(PRBool flush
);
497 PRBool
CacheFilesExist();
499 PRUint32
CalculateFileIndex(PRUint32 size
);
501 nsresult
GetBlockFileForIndex( PRUint32 index
, nsILocalFile
** result
);
502 PRUint32
GetBlockSizeForIndex( PRUint32 index
) const {
503 return BLOCK_SIZE_FOR_INDEX(index
);
506 // returns the bucket number
507 PRUint32
GetBucketIndex( PRUint32 hashNumber
) const {
508 return (hashNumber
& (kBuckets
- 1));
511 // Gets the size of the bucket (in number of records)
512 PRUint32
GetRecordsPerBucket() const {
513 return mHeader
.mRecordCount
/ kBuckets
;
516 // Gets the first record in the bucket
517 nsDiskCacheRecord
*GetFirstRecordInBucket(PRUint32 bucket
) const {
518 return mRecordArray
+ bucket
* GetRecordsPerBucket();
521 PRUint32
GetBucketRank(PRUint32 bucketIndex
, PRUint32 targetRank
);
523 PRInt32
VisitEachRecord(PRUint32 bucketIndex
,
524 nsDiskCacheRecordVisitor
* visitor
,
525 PRUint32 evictionRank
);
527 nsresult
GrowRecords();
528 nsresult
ShrinkRecords();
530 nsresult
EnsureBuffer(PRUint32 bufSize
);
532 // The returned structure will point to the buffer owned by nsDiskCacheMap,
533 // so it must not be deleted by the caller.
534 nsDiskCacheEntry
* CreateDiskCacheEntry(nsDiskCacheBinding
* binding
,
541 nsCOMPtr
<nsILocalFile
> mCacheDirectory
;
543 nsDiskCacheRecord
* mRecordArray
;
544 nsDiskCacheBlockFile mBlockFile
[3];
545 PRUint32 mBufferSize
;
547 nsDiskCacheHeader mHeader
;
548 PRInt32 mMaxRecordCount
;
551 #endif // _nsDiskCacheMap_h_