Back to index

lightning-sunbird  0.9+nobinonly
Public Member Functions | Private Member Functions | Private Attributes
nsDiskCacheMap Class Reference

#include <nsDiskCacheMap.h>

Collaboration diagram for nsDiskCacheMap:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 nsDiskCacheMap ()
 ~nsDiskCacheMap ()
nsresult Open (nsILocalFile *cacheDirectory)
 File Operations.
nsresult Close (PRBool flush)
nsresult Trim ()
nsresult FlushHeader ()
nsresult FlushRecords (PRBool unswap)
nsresult AddRecord (nsDiskCacheRecord *mapRecord, nsDiskCacheRecord *oldRecord)
 Record operations.
nsresult UpdateRecord (nsDiskCacheRecord *mapRecord)
nsresult FindRecord (PRUint32 hashNumber, nsDiskCacheRecord *mapRecord)
nsresult DeleteRecord (nsDiskCacheRecord *mapRecord)
nsresult VisitRecords (nsDiskCacheRecordVisitor *visitor)
 VisitRecords.
nsresult EvictRecords (nsDiskCacheRecordVisitor *visitor)
 EvictRecords.
nsresult DoomRecord (nsDiskCacheRecord *record)
 Disk Entry operations.
nsresult DeleteStorage (nsDiskCacheRecord *record)
nsresult DeleteRecordAndStorage (nsDiskCacheRecord *record)
nsresult GetFileForDiskCacheRecord (nsDiskCacheRecord *record, PRBool meta, nsIFile **result)
nsresult GetLocalFileForDiskCacheRecord (nsDiskCacheRecord *record, PRBool meta, nsILocalFile **result)
nsresult ReadDiskCacheEntry (nsDiskCacheRecord *record, nsDiskCacheEntry **result)
nsresult WriteDiskCacheEntry (nsDiskCacheBinding *binding)
nsresult ReadDataCacheBlocks (nsDiskCacheBinding *binding, char *buffer, PRUint32 size)
nsresult WriteDataCacheBlocks (nsDiskCacheBinding *binding, char *buffer, PRUint32 size)
nsresult DeleteStorage (nsDiskCacheRecord *record, PRBool metaData)
void IncrementTotalSize (PRInt32 delta)
 Statistical Operations.
void DecrementTotalSize (PRInt32 delta)
PRInt32 TotalSize ()
PRInt32 EntryCount ()

Private Member Functions

nsresult OpenBlockFiles ()
 Private methods.
nsresult CloseBlockFiles (PRBool flush)
PRBool CacheFilesExist ()
PRUint32 CalculateFileIndex (PRUint32 size)
nsresult GetBlockFileForIndex (PRUint32 index, nsILocalFile **result)
PRUint32 GetBlockSizeForIndex (PRUint32 index) const
PRUint32 GetBucketIndex (PRUint32 hashNumber) const
PRUint32 GetRecordsPerBucket () const
nsDiskCacheRecordGetFirstRecordInBucket (PRUint32 bucket) const
PRUint32 GetBucketRank (PRUint32 bucketIndex, PRUint32 targetRank)
 Record operations.
PRInt32 VisitEachRecord (PRUint32 bucketIndex, nsDiskCacheRecordVisitor *visitor, PRUint32 evictionRank)
nsresult GrowRecords ()
nsresult ShrinkRecords ()

Private Attributes

nsCOMPtr< nsILocalFilemCacheDirectory
 data members
PRFileDescmMapFD
nsDiskCacheRecordmRecordArray
nsDiskCacheBlockFile mBlockFile [3]
nsDiskCacheHeader mHeader

Detailed Description

Definition at line 391 of file nsDiskCacheMap.h.


Constructor & Destructor Documentation

Definition at line 394 of file nsDiskCacheMap.h.

Definition at line 395 of file nsDiskCacheMap.h.

{ (void) Close(PR_TRUE); }

Here is the call graph for this function:


Member Function Documentation

Record operations.

Definition at line 371 of file nsDiskCacheMap.cpp.

{
    const PRUint32      hashNumber = mapRecord->HashNumber();
    const PRUint32      bucketIndex = GetBucketIndex(hashNumber);
    const PRUint32      count = mHeader.mBucketUsage[bucketIndex];

    oldRecord->SetHashNumber(0);  // signify no record

    if (count == GetRecordsPerBucket()) {
        // Ignore failure to grow the record space, we will then reuse old records
        GrowRecords();
    }
    
    nsDiskCacheRecord * records = GetFirstRecordInBucket(bucketIndex);
    if (count < GetRecordsPerBucket()) {
        // stick the new record at the end
        records[count] = *mapRecord;
        mHeader.mEntryCount++;
        mHeader.mBucketUsage[bucketIndex]++;           
        if (mHeader.mEvictionRank[bucketIndex] < mapRecord->EvictionRank())
            mHeader.mEvictionRank[bucketIndex] = mapRecord->EvictionRank();
    } else {
        // Find the record with the highest eviction rank
        nsDiskCacheRecord * mostEvictable = &records[0];
        for (int i = count-1; i > 0; i--) {
            if (records[i].EvictionRank() > mostEvictable->EvictionRank())
                mostEvictable = &records[i];
        }
        *oldRecord     = *mostEvictable;    // i == GetRecordsPerBucket(), so
                                            // evict the mostEvictable
        *mostEvictable = *mapRecord;        // replace it with the new record
        // check if we need to update mostEvictable entry in header
        if (mHeader.mEvictionRank[bucketIndex] < mapRecord->EvictionRank())
            mHeader.mEvictionRank[bucketIndex] = mapRecord->EvictionRank();
        if (oldRecord->EvictionRank() >= mHeader.mEvictionRank[bucketIndex]) 
            mHeader.mEvictionRank[bucketIndex] = GetBucketRank(bucketIndex, 0);
    }

    NS_ASSERTION(mHeader.mEvictionRank[bucketIndex] == GetBucketRank(bucketIndex, 0),
                 "eviction rank out of sync");
    return NS_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 628 of file nsDiskCacheMap.cpp.

{
    nsCOMPtr<nsILocalFile> blockFile;
    nsresult rv;
    
    for (int i = 0; i < 3; ++i) {
        PRBool exists;
        rv = GetBlockFileForIndex(i, getter_AddRefs(blockFile));
        if (NS_FAILED(rv))  return PR_FALSE;

        rv = blockFile->Exists(&exists);
        if (NS_FAILED(rv) || !exists)  return PR_FALSE;
    }

    return PR_TRUE;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 982 of file nsDiskCacheMap.cpp.

{
    if (size <=  1024)  return 1;
    if (size <=  4096)  return 2;
    if (size <= 16384)  return 3;
    return 0;  
}

Here is the caller graph for this function:

Definition at line 171 of file nsDiskCacheMap.cpp.

{
    nsresult  rv = NS_OK;

    // If cache map file and its block files are still open, close them
    if (mMapFD) {
        // close block files
        rv = CloseBlockFiles(flush);
        if (NS_SUCCEEDED(rv) && flush && mRecordArray) {
            // write the map records
            rv = FlushRecords(PR_FALSE);   // don't bother swapping buckets back
            if (NS_SUCCEEDED(rv)) {
                // clear dirty bit
                mHeader.mIsDirty = PR_FALSE;
                rv = FlushHeader();
            }
        }
        if ((PR_Close(mMapFD) != PR_SUCCESS) && (NS_SUCCEEDED(rv)))
            rv = NS_ERROR_UNEXPECTED;

        mMapFD = nsnull;
    }
    PR_FREEIF(mRecordArray);
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 616 of file nsDiskCacheMap.cpp.

{
    nsresult rv, rv2 = NS_OK;
    for (int i=0; i < 3; ++i) {
        rv = mBlockFile[i].Close(flush);
        if (NS_FAILED(rv))  rv2 = rv;   // if one or more errors, report at least one
    }
    return rv2;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 456 of file nsDiskCacheMap.h.

             {
                mHeader.mDataSize -= delta;
                mHeader.mIsDirty   = PR_TRUE;
                NS_ASSERTION(mHeader.mDataSize >= 0, "disk cache size negative?");
             }

Here is the caller graph for this function:

Definition at line 463 of file nsDiskCacheMap.cpp.

{
    const PRUint32      hashNumber = mapRecord->HashNumber();
    const PRUint32      bucketIndex = GetBucketIndex(hashNumber);
    nsDiskCacheRecord * records = GetFirstRecordInBucket(bucketIndex);
    PRUint32            last = mHeader.mBucketUsage[bucketIndex]-1;

    for (int i = last; i >= 0; i--) {          
        if (records[i].HashNumber() == hashNumber) {
            // found it, now delete it.
            PRUint32  evictionRank = records[i].EvictionRank();
            NS_ASSERTION(evictionRank == mapRecord->EvictionRank(),
                         "evictionRank out of sync");
            // if not the last record, shift last record into opening
            records[i] = records[last];
            records[last].SetHashNumber(0); // clear last record
            mHeader.mBucketUsage[bucketIndex] = last;
            mHeader.mEntryCount--;

            // update eviction rank
            PRUint32  bucketIndex = GetBucketIndex(mapRecord->HashNumber());
            if (mHeader.mEvictionRank[bucketIndex] <= evictionRank) {
                mHeader.mEvictionRank[bucketIndex] = GetBucketRank(bucketIndex, 0);
            }

            NS_ASSERTION(mHeader.mEvictionRank[bucketIndex] ==
                         GetBucketRank(bucketIndex, 0), "eviction rank out of sync");
            return NS_OK;
        }
    }
    return NS_ERROR_UNEXPECTED;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 913 of file nsDiskCacheMap.cpp.

{
    nsresult  rv1 = DeleteStorage(record);
    nsresult  rv2 = DeleteRecord(record);
    return NS_FAILED(rv1) ? rv1 : rv2;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 871 of file nsDiskCacheMap.cpp.

{
    nsresult  rv1 = DeleteStorage(record, nsDiskCache::kData);
    nsresult  rv2 = DeleteStorage(record, nsDiskCache::kMetaData);
    return NS_FAILED(rv1) ? rv1 : rv2;
}

Here is the caller graph for this function:

Definition at line 880 of file nsDiskCacheMap.cpp.

{
    nsresult    rv = NS_ERROR_UNEXPECTED;
    PRUint32    fileIndex = metaData ? record->MetaFile() : record->DataFile();
    nsCOMPtr<nsIFile> file;
    
    if (fileIndex == 0) {
        // delete the file
        PRUint32  sizeK = metaData ? record->MetaFileSize() : record->DataFileSize();
        // XXX if sizeK == USHRT_MAX, stat file for actual size

        rv = GetFileForDiskCacheRecord(record, metaData, getter_AddRefs(file));
        if (NS_SUCCEEDED(rv)) {
            rv = file->Remove(PR_FALSE);    // false == non-recursive
        }
        DecrementTotalSize(sizeK * 1024);
        
    } else if (fileIndex < 4) {
        // deallocate blocks
        PRInt32  startBlock = metaData ? record->MetaStartBlock() : record->DataStartBlock();
        PRInt32  blockCount = metaData ? record->MetaBlockCount() : record->DataBlockCount();
        
        rv = mBlockFile[fileIndex - 1].DeallocateBlocks(startBlock, blockCount);
        DecrementTotalSize(blockCount * GetBlockSizeForIndex(fileIndex));
    }
    if (metaData)  record->ClearMetaLocation();
    else           record->ClearDataLocation();
    
    return rv;
}

Here is the call graph for this function:

Disk Entry operations.

Definition at line 861 of file nsDiskCacheMap.cpp.

{
    nsresult  rv = DeleteRecord(record);
    // XXX future: add record to doomed record journal

    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 465 of file nsDiskCacheMap.h.

{ return mHeader.mEntryCount; }

Here is the caller graph for this function:

EvictRecords.

Just like VisitRecords, but visits the records in order of their eviction rank

Definition at line 556 of file nsDiskCacheMap.cpp.

{
    PRUint32  tempRank[kBuckets];
    int       bucketIndex = 0;
    
    // copy eviction rank array
    for (bucketIndex = 0; bucketIndex < kBuckets; ++bucketIndex)
        tempRank[bucketIndex] = mHeader.mEvictionRank[bucketIndex];

    // Maximum number of iterations determined by number of records
    // as a safety limiter for the loop
    for (int n = 0; n < mHeader.mEntryCount; ++n) {
    
        // find bucket with highest eviction rank
        PRUint32    rank  = 0;
        for (int i = 0; i < kBuckets; ++i) {
            if (rank < tempRank[i]) {
                rank = tempRank[i];
                bucketIndex = i;
            }
        }
        
        if (rank == 0) break;  // we've examined all the records

        // visit records in bucket with eviction ranks >= target eviction rank
        if (VisitEachRecord(bucketIndex, visitor, rank) == kStopVisitingRecords)
            break;

        // find greatest rank less than 'rank'
        tempRank[bucketIndex] = GetBucketRank(bucketIndex, rank);
    }
    return NS_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 446 of file nsDiskCacheMap.cpp.

{
    const PRUint32      bucketIndex = GetBucketIndex(hashNumber);
    nsDiskCacheRecord * records = GetFirstRecordInBucket(bucketIndex);

    for (int i = mHeader.mBucketUsage[bucketIndex]-1; i >= 0; i--) {          
        if (records[i].HashNumber() == hashNumber) {
            *result = records[i];    // copy the record
            NS_ASSERTION(result->ValidRecord(), "bad cache map record");
            return NS_OK;
        }
    }
    return NS_ERROR_CACHE_KEY_NOT_FOUND;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 214 of file nsDiskCacheMap.cpp.

{
    if (!mMapFD)  return NS_ERROR_NOT_AVAILABLE;
    
    // seek to beginning of cache map
    PRInt32 filePos = PR_Seek(mMapFD, 0, PR_SEEK_SET);
    if (filePos != 0)  return NS_ERROR_UNEXPECTED;
    
    // write the header
    mHeader.Swap();
    PRInt32 bytesWritten = PR_Write(mMapFD, &mHeader, sizeof(nsDiskCacheHeader));
    mHeader.Unswap();
    if (sizeof(nsDiskCacheHeader) != bytesWritten) {
        return NS_ERROR_UNEXPECTED;
    }
    
    return NS_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 235 of file nsDiskCacheMap.cpp.

{
    if (!mMapFD)  return NS_ERROR_NOT_AVAILABLE;
    
    // seek to beginning of buckets
    PRInt32 filePos = PR_Seek(mMapFD, sizeof(nsDiskCacheHeader), PR_SEEK_SET);
    if (filePos != sizeof(nsDiskCacheHeader))
        return NS_ERROR_UNEXPECTED;
    
#if defined(IS_LITTLE_ENDIAN)
    // Swap each record
    for (PRInt32 i = 0; i < mHeader.mRecordCount; ++i) {
        if (mRecordArray[i].HashNumber())   
            mRecordArray[i].Swap();
    }
#endif
    
    PRInt32 recordArraySize = sizeof(nsDiskCacheRecord) * mHeader.mRecordCount;

    PRInt32 bytesWritten = PR_Write(mMapFD, mRecordArray, recordArraySize);
    if (bytesWritten != recordArraySize)
        return NS_ERROR_UNEXPECTED;

#if defined(IS_LITTLE_ENDIAN)
    if (unswap) {
        // Unswap each record
        for (PRInt32 i = 0; i < mHeader.mRecordCount; ++i) {
            if (mRecordArray[i].HashNumber())   
                mRecordArray[i].Unswap();
        }
    }
#endif
    
    return NS_OK;
}

Here is the caller graph for this function:

Definition at line 961 of file nsDiskCacheMap.cpp.

{
    if (!mCacheDirectory)  return NS_ERROR_NOT_AVAILABLE;
    
    nsCOMPtr<nsIFile> file;
    nsresult rv = mCacheDirectory->Clone(getter_AddRefs(file));
    if (NS_FAILED(rv))  return rv;
    
    char name[32];
    ::sprintf(name, "_CACHE_%03d_", index + 1);
    rv = file->AppendNative(nsDependentCString(name));
    if (NS_FAILED(rv))  return rv;
    
    nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
    NS_IF_ADDREF(*result = localFile);

    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRUint32 nsDiskCacheMap::GetBlockSizeForIndex ( PRUint32  index) const [inline, private]

Definition at line 480 of file nsDiskCacheMap.h.

Here is the caller graph for this function:

PRUint32 nsDiskCacheMap::GetBucketIndex ( PRUint32  hashNumber) const [inline, private]

Definition at line 485 of file nsDiskCacheMap.h.

                                                        {
        return (hashNumber & (kBuckets - 1));
    }

Here is the caller graph for this function:

PRUint32 nsDiskCacheMap::GetBucketRank ( PRUint32  bucketIndex,
PRUint32  targetRank 
) [private]

Record operations.

Definition at line 277 of file nsDiskCacheMap.cpp.

{
    nsDiskCacheRecord * records = GetFirstRecordInBucket(bucketIndex);
    PRUint32            rank = 0;

    for (int i = mHeader.mBucketUsage[bucketIndex]-1; i >= 0; i--) {          
        if ((rank < records[i].EvictionRank()) &&
            ((targetRank == 0) || (records[i].EvictionRank() < targetRank)))
                rank = records[i].EvictionRank();
    }
    return rank;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 922 of file nsDiskCacheMap.cpp.

{
    if (!mCacheDirectory)  return NS_ERROR_NOT_AVAILABLE;
    
    nsCOMPtr<nsIFile> file;
    nsresult rv = mCacheDirectory->Clone(getter_AddRefs(file));
    if (NS_FAILED(rv))  return rv;
    
    PRInt16 generation = record->Generation();
    char name[32];
    ::sprintf(name, "%08X%c%02X", record->HashNumber(),  (meta ? 'm' : 'd'), generation);
    rv = file->AppendNative(nsDependentCString(name));
    if (NS_FAILED(rv))  return rv;
    
    NS_IF_ADDREF(*result = file);
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 495 of file nsDiskCacheMap.h.

                                                                     {
        return mRecordArray + bucket * GetRecordsPerBucket();
    }

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 944 of file nsDiskCacheMap.cpp.

{
    nsCOMPtr<nsIFile> file;
    nsresult rv = GetFileForDiskCacheRecord(record, meta, getter_AddRefs(file));
    if (NS_FAILED(rv))  return rv;
    
    nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
    if (NS_FAILED(rv))  return rv;
    
    NS_IF_ADDREF(*result = localFile);
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRUint32 nsDiskCacheMap::GetRecordsPerBucket ( ) const [inline, private]

Definition at line 490 of file nsDiskCacheMap.h.

                                         {
        return mHeader.mRecordCount / kBuckets;
    }

Here is the caller graph for this function:

Definition at line 291 of file nsDiskCacheMap.cpp.

{
    if (mHeader.mRecordCount >= kMaxRecordCount)
        return NS_OK;

    // Resize the record array
    PRUint32 newCount = mHeader.mRecordCount << 1;
    if (newCount > kMaxRecordCount)
        newCount = kMaxRecordCount;
    nsDiskCacheRecord *newArray = (nsDiskCacheRecord *)
            PR_REALLOC(mRecordArray, newCount * sizeof(nsDiskCacheRecord));
    if (!newArray)
        return NS_ERROR_OUT_OF_MEMORY;

    // Space out the buckets
    PRUint32 oldRecordsPerBucket = GetRecordsPerBucket();
    PRUint32 newRecordsPerBucket = newCount / kBuckets;
    // Work from back to space out each bucket to the new array
    for (int bucketIndex = kBuckets - 1; bucketIndex >= 0; --bucketIndex) {
        // Move bucket
        nsDiskCacheRecord *newRecords = newArray + bucketIndex * newRecordsPerBucket;
        const PRUint32 count = mHeader.mBucketUsage[bucketIndex];
        memmove(newRecords,
                newArray + bucketIndex * oldRecordsPerBucket,
                count * sizeof(nsDiskCacheRecord));
        // Clear the new empty entries
        for (PRUint32 i = count; i < newRecordsPerBucket; ++i)
            newRecords[i].SetHashNumber(0);
    }

    // Set as the new record array
    mRecordArray = newArray;
    mHeader.mRecordCount = newCount;
    return NS_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Statistical Operations.

Definition at line 449 of file nsDiskCacheMap.h.

             {
                NS_ASSERTION(mHeader.mDataSize >= 0, "disk cache size negative?");
                mHeader.mDataSize += delta;
                mHeader.mIsDirty   = PR_TRUE;
             }

Here is the caller graph for this function:

File Operations.

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.

Definition at line 62 of file nsDiskCacheMap.cpp.

{
    NS_ENSURE_ARG_POINTER(cacheDirectory);
    if (mMapFD)  return NS_ERROR_ALREADY_INITIALIZED;

    mCacheDirectory = cacheDirectory;   // save a reference for ourselves
    
    // create nsILocalFile for _CACHE_MAP_
    nsresult rv;
    nsCOMPtr<nsIFile> file;
    rv = cacheDirectory->Clone(getter_AddRefs(file));
    nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(file, &rv));
    if (NS_FAILED(rv))  return rv;
    rv = localFile->AppendNative(NS_LITERAL_CSTRING("_CACHE_MAP_"));
    if (NS_FAILED(rv))  return rv;

    // open the file - restricted to user, the data could be confidential
    rv = localFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mMapFD);
    if (NS_FAILED(rv))  return NS_ERROR_FILE_CORRUPTED;

    PRBool cacheFilesExist = CacheFilesExist();
    rv = NS_ERROR_FILE_CORRUPTED;  // presume the worst

    // check size of map file
    PRUint32 mapSize = PR_Available(mMapFD);    
    if (mapSize == 0) {  // creating a new _CACHE_MAP_

        // block files shouldn't exist if we're creating the _CACHE_MAP_
        if (cacheFilesExist)
            goto error_exit;

        // create the file - initialize in memory
        memset(&mHeader, 0, sizeof(nsDiskCacheHeader));
        mHeader.mVersion = nsDiskCache::kCurrentVersion;
        mHeader.mRecordCount = kMinRecordCount;
        mRecordArray = (nsDiskCacheRecord *)
            PR_CALLOC(mHeader.mRecordCount * sizeof(nsDiskCacheRecord));
        if (!mRecordArray) {
            rv = NS_ERROR_OUT_OF_MEMORY;
            goto error_exit;
        }
    } else if (mapSize >= sizeof(nsDiskCacheHeader)) {  // read existing _CACHE_MAP_
        
        // if _CACHE_MAP_ exists, so should the block files
        if (!cacheFilesExist)
            goto error_exit;

        // read the header
        PRUint32 bytesRead = PR_Read(mMapFD, &mHeader, sizeof(nsDiskCacheHeader));
        if (sizeof(nsDiskCacheHeader) != bytesRead)  goto error_exit;
        mHeader.Unswap();

        if (mHeader.mIsDirty || (mHeader.mVersion != nsDiskCache::kCurrentVersion))
            goto error_exit;

        PRUint32 recordArraySize =
                mHeader.mRecordCount * sizeof(nsDiskCacheRecord);
        if (mapSize < recordArraySize + sizeof(nsDiskCacheHeader))
            goto error_exit;

        // Get the space for the records
        mRecordArray = (nsDiskCacheRecord *) PR_MALLOC(recordArraySize);
        if (!mRecordArray) {
            rv = NS_ERROR_OUT_OF_MEMORY;
            goto error_exit;
        }

        // Read the records
        bytesRead = PR_Read(mMapFD, mRecordArray, recordArraySize);
        if (bytesRead < recordArraySize)
            goto error_exit;

        // Unswap each record
        PRInt32 total = 0;
        for (PRInt32 i = 0; i < mHeader.mRecordCount; ++i) {
            if (mRecordArray[i].HashNumber()) {
#if defined(IS_LITTLE_ENDIAN)
                mRecordArray[i].Unswap();
#endif
                total ++;
            }
        }
        
        // verify entry count
        if (total != mHeader.mEntryCount)
            goto error_exit;

    } else {
        goto error_exit;
    }

    rv = OpenBlockFiles();
    if (NS_FAILED(rv))  goto error_exit;

    // set dirty bit and flush header
    mHeader.mIsDirty    = PR_TRUE;
    rv = FlushHeader();
    if (NS_FAILED(rv))  goto error_exit;
    
    return NS_OK;
    
error_exit:
    (void) Close(PR_FALSE);
       
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Private methods.

Definition at line 593 of file nsDiskCacheMap.cpp.

{
    // create nsILocalFile for block file
    nsCOMPtr<nsILocalFile> blockFile;
    nsresult rv;
    
    for (int i = 0; i < 3; ++i) {
        rv = GetBlockFileForIndex(i, getter_AddRefs(blockFile));
        if (NS_FAILED(rv))  goto error_exit;
    
        PRUint32 blockSize = GetBlockSizeForIndex(i+1); // +1 to match file selectors 1,2,3
        rv = mBlockFile[i].Open(blockFile, blockSize);
        if (NS_FAILED(rv)) goto error_exit;
    }
    return NS_OK;

error_exit:
    (void)CloseBlockFiles(PR_FALSE); // we already have an error to report
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

nsresult nsDiskCacheMap::ReadDataCacheBlocks ( nsDiskCacheBinding binding,
char *  buffer,
PRUint32  size 
)

Definition at line 810 of file nsDiskCacheMap.cpp.

{
    nsresult  rv;
    PRUint32  fileIndex = binding->mRecord.DataFile();
    PRUint32  blockSize = GetBlockSizeForIndex(fileIndex);
    PRUint32  blockCount = binding->mRecord.DataBlockCount();
    PRUint32  minSize = blockSize * blockCount;
    
    if (size < minSize) {
        NS_WARNING("buffer too small");
        return NS_ERROR_UNEXPECTED;
    }
    
    rv = mBlockFile[fileIndex - 1].ReadBlocks(buffer,
                                              binding->mRecord.DataStartBlock(),
                                              blockCount);
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 647 of file nsDiskCacheMap.cpp.

{
    nsresult            rv         = NS_ERROR_UNEXPECTED;
    nsDiskCacheEntry *  diskEntry  = nsnull;
    PRUint32            metaFile   = record->MetaFile();
    PRFileDesc *        fd         = nsnull;
    *result = nsnull;
    
    if (!record->MetaLocationInitialized())  return NS_ERROR_NOT_AVAILABLE;
    
    if (metaFile == 0) {  // entry/metadata stored in separate file
        // open and read the file
        nsCOMPtr<nsILocalFile> file;
        rv = GetLocalFileForDiskCacheRecord(record, nsDiskCache::kMetaData, getter_AddRefs(file));
        if (NS_FAILED(rv))  return rv;

        PRFileDesc * fd = nsnull;
        // open the file - restricted to user, the data could be confidential
        rv = file->OpenNSPRFileDesc(PR_RDONLY, 00600, &fd);
        if (NS_FAILED(rv))  return rv;
        
        PRInt32 fileSize = PR_Available(fd);
        if (fileSize < 0) {
            // an error occurred. We could call PR_GetError(), but how would that help?
            rv = NS_ERROR_UNEXPECTED;
            goto exit;
        }

        diskEntry = (nsDiskCacheEntry *) new char[fileSize];
        if (!diskEntry) {
            rv = NS_ERROR_OUT_OF_MEMORY;
            goto exit;
        }
        
        PRInt32 bytesRead = PR_Read(fd, diskEntry, fileSize);
        if (bytesRead < fileSize) {
            rv = NS_ERROR_UNEXPECTED;
            goto exit;
        }

    } else if (metaFile < 4) {  // XXX magic number: use constant
        // entry/metadata stored in cache block file
        
        // allocate buffer
        PRUint32 blockSize  = GetBlockSizeForIndex(metaFile);
        PRUint32 blockCount = record->MetaBlockCount();
        diskEntry = (nsDiskCacheEntry *) new char[blockSize * blockCount];
        
        // read diskEntry
        rv = mBlockFile[metaFile - 1].ReadBlocks((char *)diskEntry,
                                                 record->MetaStartBlock(),
                                                 blockCount);
        if (NS_FAILED(rv))  goto exit;
    }
    
    diskEntry->Unswap();    // disk to memory
    // pass ownership to caller
    *result = diskEntry;
    diskEntry = nsnull;

exit:
    // XXX auto ptr would be nice
    if (fd) (void) PR_Close(fd);
    delete [] (char *)diskEntry;
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 328 of file nsDiskCacheMap.cpp.

{
    if (mHeader.mRecordCount <= kMinRecordCount)
        return NS_OK;

    // Verify if we can shrink the record array: all buckets must be less than
    // 1/2 filled
    PRUint32 maxUsage = 0, bucketIndex;
    for (bucketIndex = 0; bucketIndex < kBuckets; ++bucketIndex) {
        if (maxUsage < mHeader.mBucketUsage[bucketIndex])
            maxUsage = mHeader.mBucketUsage[bucketIndex];
    }
    // Determine new bucket size, halve size until maxUsage
    PRUint32 oldRecordsPerBucket = GetRecordsPerBucket();
    PRUint32 newRecordsPerBucket = oldRecordsPerBucket;
    while (maxUsage < (newRecordsPerBucket >> 1))
        newRecordsPerBucket >>= 1;
    if (newRecordsPerBucket < kMinRecordCount) 
        newRecordsPerBucket = kMinRecordCount;
    if (newRecordsPerBucket == oldRecordsPerBucket)
        return NS_OK;
    // Move the buckets close to each other
    for (bucketIndex = 0; bucketIndex < kBuckets; ++bucketIndex) {
        // Move bucket
        memmove(mRecordArray + bucketIndex * newRecordsPerBucket,
                mRecordArray + bucketIndex * oldRecordsPerBucket,
                mHeader.mBucketUsage[bucketIndex] * sizeof(nsDiskCacheRecord));
    }

    // Shrink the record array memory block itself
    PRUint32 newCount = newRecordsPerBucket * kBuckets;
    nsDiskCacheRecord* newArray = (nsDiskCacheRecord *)
            PR_REALLOC(mRecordArray, newCount * sizeof(nsDiskCacheRecord));
    if (!newArray)
        return NS_ERROR_OUT_OF_MEMORY;

    // Set as the new record array
    mRecordArray = newArray;
    mHeader.mRecordCount = newCount;
    return NS_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 463 of file nsDiskCacheMap.h.

{ return mHeader.mDataSize; }

Here is the caller graph for this function:

Definition at line 199 of file nsDiskCacheMap.cpp.

{
    nsresult rv, rv2 = NS_OK;
    for (int i=0; i < 3; ++i) {
        rv = mBlockFile[i].Trim();
        if (NS_FAILED(rv))  rv2 = rv;   // if one or more errors, report at least one
    }
    // Try to shrink the records array
    rv = ShrinkRecords();
    if (NS_FAILED(rv))  rv2 = rv;   // if one or more errors, report at least one
    return rv2;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 417 of file nsDiskCacheMap.cpp.

{
    const PRUint32      hashNumber = mapRecord->HashNumber();
    const PRUint32      bucketIndex = GetBucketIndex(hashNumber);
    nsDiskCacheRecord * records = GetFirstRecordInBucket(bucketIndex);

    for (int i = mHeader.mBucketUsage[bucketIndex]-1; i >= 0; i--) {          
        if (records[i].HashNumber() == hashNumber) {
            const PRUint32 oldRank = records[i].EvictionRank();

            // stick the new record here            
            records[i] = *mapRecord;

            // update eviction rank in header if necessary
            if (mHeader.mEvictionRank[bucketIndex] < mapRecord->EvictionRank())
                mHeader.mEvictionRank[bucketIndex] = mapRecord->EvictionRank();
            else if (mHeader.mEvictionRank[bucketIndex] == oldRank)
                mHeader.mEvictionRank[bucketIndex] = GetBucketRank(bucketIndex, 0);

NS_ASSERTION(mHeader.mEvictionRank[bucketIndex] == GetBucketRank(bucketIndex, 0),
             "eviction rank out of sync");
            return NS_OK;
        }
    }
    return NS_ERROR_UNEXPECTED;
}

Here is the call graph for this function:

Here is the caller graph for this function:

PRInt32 nsDiskCacheMap::VisitEachRecord ( PRUint32  bucketIndex,
nsDiskCacheRecordVisitor visitor,
PRUint32  evictionRank 
) [private]

Definition at line 498 of file nsDiskCacheMap.cpp.

{
    PRInt32             rv = kVisitNextRecord;
    PRUint32            count = mHeader.mBucketUsage[bucketIndex];
    nsDiskCacheRecord * records = GetFirstRecordInBucket(bucketIndex);

    // call visitor for each entry (matching any eviction rank)
    for (int i = count-1; i >= 0; i--) {
        if (evictionRank > records[i].EvictionRank()) continue;

        rv = visitor->VisitRecord(&records[i]);
        if (rv == kStopVisitingRecords) 
            break;    // Stop visiting records
        
        if (rv == kDeleteRecordAndContinue) {
            --count;
            records[i] = records[count];
            records[count].SetHashNumber(0);
        }
    }

    if (mHeader.mBucketUsage[bucketIndex] - count != 0) {
        mHeader.mEntryCount -= mHeader.mBucketUsage[bucketIndex] - count;
        mHeader.mBucketUsage[bucketIndex] = count;
        // recalc eviction rank
        mHeader.mEvictionRank[bucketIndex] = GetBucketRank(bucketIndex, 0);
    }
    NS_ASSERTION(mHeader.mEvictionRank[bucketIndex] ==
                 GetBucketRank(bucketIndex, 0), "eviction rank out of sync");

    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

VisitRecords.

Visit every record in cache map in the most convenient order

Definition at line 540 of file nsDiskCacheMap.cpp.

{
    for (int bucketIndex = 0; bucketIndex < kBuckets; ++bucketIndex) {
        if (VisitEachRecord(bucketIndex, visitor, 0) == kStopVisitingRecords)
            break;
    }   
    return NS_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

nsresult nsDiskCacheMap::WriteDataCacheBlocks ( nsDiskCacheBinding binding,
char *  buffer,
PRUint32  size 
)

Definition at line 831 of file nsDiskCacheMap.cpp.

{
    nsresult  rv;
    
    // determine block file & number of blocks
    PRUint32  fileIndex  = CalculateFileIndex(size);
    PRUint32  blockSize  = GetBlockSizeForIndex(fileIndex);
    PRUint32  blockCount = 0;
    PRInt32   startBlock = 0;
    
    if (size > 0) {
        blockCount = ((size - 1) / blockSize) + 1;
        startBlock = mBlockFile[fileIndex - 1].AllocateBlocks(blockCount);

        rv = mBlockFile[fileIndex - 1].WriteBlocks(buffer, startBlock, blockCount);
        if (NS_FAILED(rv))  return rv;
        
        IncrementTotalSize(blockCount * blockSize);
    }
    
    
    // update binding and cache map record
    binding->mRecord.SetDataBlocks(fileIndex, startBlock, blockCount);
    rv = UpdateRecord(&binding->mRecord);

    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 716 of file nsDiskCacheMap.cpp.

{
    nsresult            rv        = NS_OK;
    nsDiskCacheEntry *  diskEntry =  CreateDiskCacheEntry(binding);
    if (!diskEntry)  return NS_ERROR_UNEXPECTED;
    
    PRUint32  size      = diskEntry->Size();
    PRUint32  fileIndex = CalculateFileIndex(size);

    // Deallocate old storage if necessary    
    if (binding->mRecord.MetaLocationInitialized()) {
        // we have existing storage

        if ((binding->mRecord.MetaFile() == 0) &&
            (fileIndex == 0)) {  // keeping the separate file
            // just decrement total
            // XXX if bindRecord.MetaFileSize == USHRT_MAX, stat the file to see how big it is
            DecrementTotalSize(binding->mRecord.MetaFileSize() * 1024);
            NS_ASSERTION(binding->mRecord.MetaFileGeneration() == binding->mGeneration,
                         "generations out of sync");
        } else {
            rv = DeleteStorage(&binding->mRecord, nsDiskCache::kMetaData);
            if (NS_FAILED(rv))  goto exit;
        }
    }

    binding->mRecord.SetEvictionRank(ULONG_MAX - SecondsFromPRTime(PR_Now()));
        
    if (fileIndex == 0) {
        // Write entry data to separate file
        PRUint32 metaFileSizeK = ((size + 0x03FF) >> 10); // round up to nearest 1k
        nsCOMPtr<nsILocalFile> localFile;
        
        // XXX handle metaFileSizeK > USHRT_MAX
        binding->mRecord.SetMetaFileGeneration(binding->mGeneration);
        binding->mRecord.SetMetaFileSize(metaFileSizeK);
        rv = UpdateRecord(&binding->mRecord);
        if (NS_FAILED(rv))  goto exit;

        rv = GetLocalFileForDiskCacheRecord(&binding->mRecord,
                                            nsDiskCache::kMetaData,
                                            getter_AddRefs(localFile));
        if (NS_FAILED(rv))  goto exit;
        
        // open the file
        PRFileDesc * fd;
        // open the file - restricted to user, the data could be confidential
        rv = localFile->OpenNSPRFileDesc(PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE, 00600, &fd);
        if (NS_FAILED(rv))  goto exit;  // unable to open or create file

        // write the file
        diskEntry->Swap();
        PRInt32 bytesWritten = PR_Write(fd, diskEntry, size);
        
        PRStatus err = PR_Close(mMapFD);
        if ((bytesWritten != (PRInt32)size) || (err != PR_SUCCESS)) {
            rv = NS_ERROR_UNEXPECTED;
            goto exit;
        }
        // XXX handle metaFileSizeK == USHRT_MAX
        IncrementTotalSize(metaFileSizeK * 1024);
        
    } else {
        PRUint32  blockSize = GetBlockSizeForIndex(fileIndex);
        PRUint32  blocks    = ((size - 1) / blockSize) + 1;

        // write entry data to disk cache block file
        PRInt32 startBlock = mBlockFile[fileIndex - 1].AllocateBlocks(blocks);
        if (startBlock < 0) {
            rv = NS_ERROR_UNEXPECTED;
            goto exit;
        }
        
        // update binding and cache map record
        binding->mRecord.SetMetaBlocks(fileIndex, startBlock, blocks);
        rv = UpdateRecord(&binding->mRecord);
        if (NS_FAILED(rv))  goto exit;
        // XXX we should probably write out bucket ourselves

        // write data
        diskEntry->Swap();
        rv = mBlockFile[fileIndex - 1].WriteBlocks(diskEntry, startBlock, blocks);
        if (NS_FAILED(rv))  goto exit;
        
        IncrementTotalSize(blocks * blockSize);
    }

exit:
    delete [] (char *)diskEntry;
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

Definition at line 515 of file nsDiskCacheMap.h.

data members

Definition at line 512 of file nsDiskCacheMap.h.

Definition at line 516 of file nsDiskCacheMap.h.

Definition at line 513 of file nsDiskCacheMap.h.

Definition at line 514 of file nsDiskCacheMap.h.


The documentation for this class was generated from the following files: