(no bug) fix end-of-line whitespace so I can add a changeset with an a=bustage annotation
[mozilla-central.git] / netwerk / cache / nsDiskCacheMap.h
blob78676c9bad54b8475d31a9bc9242da8d5b3fc385
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
14 * License.
16 * The Original Code is nsDiskCacheMap.h, released
17 * March 23, 2001.
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.
24 * Contributor(s):
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_
45 #include <limits.h>
47 #include "prtypes.h"
48 #include "prnetdb.h"
49 #include "nsDebug.h"
50 #include "nsError.h"
51 #include "nsILocalFile.h"
53 #include "nsDiskCache.h"
54 #include "nsDiskCacheBlockFile.h"
57 class nsDiskCacheBinding;
58 struct nsDiskCacheEntry;
60 /******************************************************************************
61 * nsDiskCacheRecord
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
75 * File Selector:
76 * 0 = separate file on disk
77 * 1 = 256 byte block file
78 * 2 = 1k block file
79 * 3 = 4k 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 {
99 private:
100 PRUint32 mHashNumber;
101 PRUint32 mEvictionRank;
102 PRUint32 mDataLocation;
103 PRUint32 mMetaLocation;
105 enum {
106 eLocationInitializedMask = 0x80000000,
108 eLocationSelectorMask = 0x30000000,
109 eLocationSelectorOffset = 28,
111 eExtraBlocksMask = 0x03000000,
112 eExtraBlocksOffset = 24,
114 eReservedMask = 0x4C000000,
116 eBlockNumberMask = 0x00FFFFFF,
118 eFileSizeMask = 0x00FFFF00,
119 eFileSizeOffset = 8,
120 eFileGenerationMask = 0x000000FF,
121 eFileReservedMask = 0x4F000000
125 public:
126 nsDiskCacheRecord()
127 : mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
131 PRBool ValidRecord()
133 if ((mDataLocation & eReservedMask) || (mMetaLocation & eReservedMask))
134 return PR_FALSE;
135 return PR_TRUE;
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)
157 // clear everything
158 mDataLocation = 0;
160 // set file index
161 NS_ASSERTION( index < 4,"invalid location index");
162 NS_ASSERTION( index > 0,"invalid location index");
163 mDataLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
165 // set startBlock
166 NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
167 mDataLocation |= startBlock & eBlockNumberMask;
169 // set blockCount
170 NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
171 --blockCount;
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)
208 mDataLocation = 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)
225 // clear everything
226 mMetaLocation = 0;
228 // set file index
229 NS_ASSERTION( index < 4, "invalid location index");
230 NS_ASSERTION( index > 0, "invalid location index");
231 mMetaLocation |= (index << eLocationSelectorOffset) & eLocationSelectorMask;
233 // set startBlock
234 NS_ASSERTION(startBlock == (startBlock & eBlockNumberMask), "invalid block number");
235 mMetaLocation |= startBlock & eBlockNumberMask;
237 // set blockCount
238 NS_ASSERTION( (blockCount>=1) && (blockCount<=4),"invalid block count");
239 --blockCount;
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)
275 mMetaLocation = 0;
276 mMetaLocation |= generation & eFileGenerationMask;
277 mMetaLocation |= eLocationInitializedMask;
280 PRUint8 Generation() const
282 if ((mDataLocation & eLocationInitializedMask) &&
283 (DataFile() == 0))
284 return DataFileGeneration();
286 if ((mMetaLocation & eLocationInitializedMask) &&
287 (MetaFile() == 0))
288 return MetaFileGeneration();
290 return 0; // no generation
293 #if defined(IS_LITTLE_ENDIAN)
294 void Swap()
296 mHashNumber = htonl(mHashNumber);
297 mEvictionRank = htonl(mEvictionRank);
298 mDataLocation = htonl(mDataLocation);
299 mMetaLocation = htonl(mMetaLocation);
301 #endif
303 #if defined(IS_LITTLE_ENDIAN)
304 void Unswap()
306 mHashNumber = ntohl(mHashNumber);
307 mEvictionRank = ntohl(mEvictionRank);
308 mDataLocation = ntohl(mDataLocation);
309 mMetaLocation = ntohl(mMetaLocation);
311 #endif
316 /******************************************************************************
317 * nsDiskCacheRecordVisitor
318 *****************************************************************************/
320 enum { kDeleteRecordAndContinue = -1,
321 kStopVisitingRecords = 0,
322 kVisitNextRecord = 1
325 class nsDiskCacheRecordVisitor {
326 public:
328 virtual PRInt32 VisitRecord( nsDiskCacheRecord * mapRecord) = 0;
332 /******************************************************************************
333 * nsDiskCacheHeader
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
345 nsDiskCacheHeader()
346 : mVersion(nsDiskCache::kCurrentVersion)
347 , mDataSize(0)
348 , mEntryCount(0)
349 , mIsDirty(PR_TRUE)
350 , mRecordCount(0)
353 void Swap()
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]);
366 #endif
369 void Unswap()
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]);
382 #endif
387 /******************************************************************************
388 * nsDiskCacheMap
389 *****************************************************************************/
391 class nsDiskCacheMap {
392 public:
394 nsDiskCacheMap() :
395 mCacheDirectory(nsnull),
396 mMapFD(nsnull),
397 mRecordArray(nsnull),
398 mBufferSize(0),
399 mBuffer(nsnull),
400 mMaxRecordCount(16384) // this default value won't matter
403 ~nsDiskCacheMap() {
404 (void) Close(PR_TRUE);
408 * File Operations
410 * Open
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);
417 nsresult Trim();
419 nsresult FlushHeader();
420 nsresult FlushRecords( PRBool unswap);
422 void NotifyCapacityChange(PRUint32 capacity);
425 * Record operations
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,
440 PRBool meta,
441 nsIFile ** result);
443 nsresult GetLocalFileForDiskCacheRecord( nsDiskCacheRecord * record,
444 PRBool meta,
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; }
490 private:
493 * Private methods
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,
535 PRUint32 * size);
538 * data members
540 private:
541 nsCOMPtr<nsILocalFile> mCacheDirectory;
542 PRFileDesc * mMapFD;
543 nsDiskCacheRecord * mRecordArray;
544 nsDiskCacheBlockFile mBlockFile[3];
545 PRUint32 mBufferSize;
546 char * mBuffer;
547 nsDiskCacheHeader mHeader;
548 PRInt32 mMaxRecordCount;
551 #endif // _nsDiskCacheMap_h_