Additional refactoring of disk cache.

git-svn-id: svn://10.0.0.236/branches/DISKCACHE2_BRANCH@94893 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
gordon%netscape.com
2001-05-14 20:04:53 +00:00
parent 826ed51db3
commit 8a22ddce2f
11 changed files with 1477 additions and 830 deletions

View File

@@ -110,11 +110,11 @@ nsCacheEntry::~nsCacheEntry()
nsresult
nsCacheEntry::CreateCacheEntry( const char * key,
PRBool streamBased,
nsCacheStoragePolicy storagePolicy,
nsCacheDevice * device,
nsCacheEntry ** result)
nsCacheEntry::Create( const char * key,
PRBool streamBased,
nsCacheStoragePolicy storagePolicy,
nsCacheDevice * device,
nsCacheEntry ** result)
{
nsCString* newKey = new nsCString(key);
if (!newKey) return NS_ERROR_OUT_OF_MEMORY;

View File

@@ -55,11 +55,11 @@ public:
~nsCacheEntry();
static nsresult CreateCacheEntry( const char * key,
PRBool streamBased,
nsCacheStoragePolicy storagePolicy,
nsCacheDevice * device,
nsCacheEntry ** result);
static nsresult Create( const char * key,
PRBool streamBased,
nsCacheStoragePolicy storagePolicy,
nsCacheDevice * device,
nsCacheEntry ** result);
nsCString * Key() { return mKey; }

View File

@@ -26,5 +26,22 @@
#ifndef _nsDiskCache_h_
#define _nsDiskCache_h_
#include "nsCacheEntry.h"
class nsDiskCacheBindData;
nsDiskCacheBindData *
GetBindDataFromCacheEntry(nsCacheEntry * entry);
class nsDiskCache {
public:
enum {
kCurrentVersion = 0x00010003 // XXX whats the format?
};
enum { kData, kMetaData };
static PLDHashNumber Hash(const char* key);
};
#endif // _nsDiskCache_h_

View File

@@ -26,27 +26,51 @@
#include "nsDiskCacheBindData.h"
/******************************************************************************
* Utility Functions
*****************************************************************************/
nsDiskCacheBindData *
GetBindDataFromCacheEntry(nsCacheEntry * entry)
{
nsCOMPtr<nsISupports> data;
nsresult rv = entry->GetData(getter_AddRefs(data));
if (NS_FAILED(rv)) return nsnull;
return (nsDiskCacheBindData *)data.get();
}
/******************************************************************************
* nsDiskCacheBindData
*****************************************************************************/
NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBindData);
PLDHashNumber
nsDiskCacheBindData::Hash(const char* key)
nsDiskCacheBindData::nsDiskCacheBindData(nsCacheEntry* entry)
: mCacheEntry(entry)
{
PLDHashNumber h = 0;
for (const PRUint8* s = (PRUint8*) key; *s != '\0'; ++s)
h = (h >> (PL_DHASH_BITS - 4)) ^ (h << 4) ^ *s;
return (h == 0 ? ULONG_MAX : h);
NS_INIT_ISUPPORTS();
PR_INIT_CLIST(this);
mRecord.SetHashNumber(nsDiskCache::Hash(entry->Key()->get()));
}
nsDiskCacheBindData::~nsDiskCacheBindData()
{
// XXX if PR_CLIST_IS_EMPTY(this) then remove entry from hashtable
PR_REMOVE_LINK(this); // XXX why are we still on a list?
}
/******************************************************************************
* nsDiskCacheHashTable
* nsDiskCacheBindery
*
* Keeps track of bound disk cache entries to detect for collisions.
*
*****************************************************************************/
PLDHashTableOps nsDiskCacheHashTable::ops =
PLDHashTableOps nsDiskCacheBindery::ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
@@ -59,13 +83,13 @@ PLDHashTableOps nsDiskCacheHashTable::ops =
};
nsDiskCacheHashTable::nsDiskCacheHashTable()
nsDiskCacheBindery::nsDiskCacheBindery()
: initialized(PR_FALSE)
{
}
nsDiskCacheHashTable::~nsDiskCacheHashTable()
nsDiskCacheBindery::~nsDiskCacheBindery()
{
if (initialized)
PL_DHashTableFinish(&table);
@@ -73,7 +97,7 @@ nsDiskCacheHashTable::~nsDiskCacheHashTable()
nsresult
nsDiskCacheHashTable::Init()
nsDiskCacheBindery::Init()
{
nsresult rv = NS_OK;
initialized = PL_DHashTableInit(&table, &ops, nsnull,
@@ -85,137 +109,171 @@ nsDiskCacheHashTable::Init()
}
// XXX need to have nsDiskCacheRecord passed in
nsDiskCacheBindData *
nsDiskCacheHashTable::GetEntry(const char * key)
nsDiskCacheBindery::CreateBindDataForCacheEntry(nsCacheEntry * entry)
{
return GetEntry(nsDiskCacheBindData::Hash(key));
nsCOMPtr<nsISupports> data;
nsresult rv = entry->GetData(getter_AddRefs(data));
if (NS_FAILED(rv) || data) {
NS_ASSERTION(!data, "cache entry already has bind data");
return nsnull;
}
nsDiskCacheBindData * bindData = new nsDiskCacheBindData(entry);
if (!bindData) return nsnull;
data = bindData; // add ref
entry->SetData(data.get()); // XXX why .get() ?
// XXX add bindData to collision detection system
return bindData;
}
// XXX if we read an entry off of disk (FindEntry)
// XXX - it may already have a generation number
// XXX - generation number conflict is an error
// XXX new entries (BindEntry)
// XXX - assign generation number
// XXX FindActiveBindData(hashNumber) // there can be only one
// XXX FindBindData(hashNumber, generation)
// XXX UnbindEntry(nsDiskCacheBindData * bindData); // called from DeactivateEntry()
nsDiskCacheBindData *
nsDiskCacheBindery::GetEntry(const char * key)
{
return GetEntry(nsDiskCache::Hash(key));
}
nsDiskCacheBindData *
nsDiskCacheHashTable::GetEntry(PLDHashNumber key)
nsDiskCacheBindery::GetEntry(PLDHashNumber key)
{
nsDiskCacheBindData * result = nsnull;
NS_ASSERTION(initialized, "nsDiskCacheHashTable not initialized");
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
HashTableEntry * hashEntry;
hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table, (void*) key, PL_DHASH_LOOKUP);
if (PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
result = hashEntry->mDiskCacheBindData;
result = hashEntry->mBindData;
}
return result;
}
nsresult
nsDiskCacheHashTable::AddEntry(nsDiskCacheBindData * entry)
nsDiskCacheBindery::AddEntry(nsDiskCacheBindData * bindData)
{
NS_ENSURE_ARG_POINTER(entry);
NS_ASSERTION(initialized, "nsDiskCacheHashTable not initialized");
NS_ENSURE_ARG_POINTER(bindData);
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
HashTableEntry * hashEntry;
hashEntry = (HashTableEntry *) PL_DHashTableOperate(&table,
(void*) entry->getHashNumber(),
(void*) bindData->mRecord.HashNumber(),
PL_DHASH_ADD);
if (!hashEntry) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(hashEntry->mDiskCacheBindData = entry);
NS_ADDREF(hashEntry->mBindData = bindData);
return NS_OK;
}
void
nsDiskCacheHashTable::RemoveEntry(nsDiskCacheBindData * entry)
nsDiskCacheBindery::RemoveEntry(nsDiskCacheBindData * bindData)
{
NS_ASSERTION(initialized, "nsDiskCacheHashTable not initialized");
NS_ASSERTION(entry, "### cacheEntry == nsnull");
NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
NS_ASSERTION(bindData, "### bindData == nsnull");
(void) PL_DHashTableOperate(&table, (void*) entry->getHashNumber(), PL_DHASH_REMOVE);
(void) PL_DHashTableOperate(&table, (void*) bindData->mRecord.HashNumber(), PL_DHASH_REMOVE);
}
void
nsDiskCacheHashTable::VisitEntries(Visitor *visitor)
nsDiskCacheBindery::VisitEntries(Visitor *visitor)
{
PL_DHashTableEnumerate(&table, VisitEntry, visitor);
}
PLDHashOperator PR_CALLBACK
nsDiskCacheHashTable::VisitEntry(PLDHashTable * table,
PLDHashEntryHdr * header,
PRUint32 number,
void * arg)
nsDiskCacheBindery::VisitEntry(PLDHashTable * table,
PLDHashEntryHdr * header,
PRUint32 number,
void * arg)
{
HashTableEntry* hashEntry = (HashTableEntry *) header;
Visitor *visitor = (Visitor*) arg;
return (visitor->VisitEntry(hashEntry->mDiskCacheBindData) ? PL_DHASH_NEXT : PL_DHASH_STOP);
return (visitor->VisitEntry(hashEntry->mBindData) ? PL_DHASH_NEXT : PL_DHASH_STOP);
}
/**
* hash table operation callback functions
*/
const void * PR_CALLBACK
nsDiskCacheHashTable::GetKey(PLDHashTable * /*table*/, PLDHashEntryHdr * header)
nsDiskCacheBindery::GetKey(PLDHashTable * /*table*/, PLDHashEntryHdr * header)
{
HashTableEntry * hashEntry = (HashTableEntry *) header;
return (void*) hashEntry->mDiskCacheBindData->getHashNumber();
return (void*) hashEntry->mBindData->mRecord.HashNumber();
}
PLDHashNumber PR_CALLBACK
nsDiskCacheHashTable::HashKey( PLDHashTable *table, const void *key)
nsDiskCacheBindery::HashKey( PLDHashTable *table, const void *key)
{
return (PLDHashNumber) key;
}
PRBool PR_CALLBACK
nsDiskCacheHashTable::MatchEntry(PLDHashTable * /* table */,
const PLDHashEntryHdr * header,
const void * key)
nsDiskCacheBindery::MatchEntry(PLDHashTable * /* table */,
const PLDHashEntryHdr * header,
const void * key)
{
HashTableEntry * hashEntry = (HashTableEntry *) header;
return (hashEntry->mDiskCacheBindData->getHashNumber() == (PLDHashNumber) key);
return (hashEntry->mBindData->mRecord.HashNumber() == (PLDHashNumber) key);
}
void PR_CALLBACK
nsDiskCacheHashTable::MoveEntry(PLDHashTable * /* table */,
const PLDHashEntryHdr * fromHeader,
PLDHashEntryHdr * toHeader)
nsDiskCacheBindery::MoveEntry(PLDHashTable * /* table */,
const PLDHashEntryHdr * fromHeader,
PLDHashEntryHdr * toHeader)
{
HashTableEntry * fromEntry = (HashTableEntry *) fromHeader;
HashTableEntry * toEntry = (HashTableEntry *) toHeader;
toEntry->keyHash = fromEntry->keyHash;
toEntry->mDiskCacheBindData = fromEntry->mDiskCacheBindData;
fromEntry->mDiskCacheBindData = nsnull;
toEntry->mBindData = fromEntry->mBindData;
fromEntry->mBindData = nsnull;
}
void PR_CALLBACK
nsDiskCacheHashTable::ClearEntry(PLDHashTable * /* table */,
PLDHashEntryHdr * header)
nsDiskCacheBindery::ClearEntry(PLDHashTable * /* table */,
PLDHashEntryHdr * header)
{
HashTableEntry* hashEntry = (HashTableEntry *) header;
hashEntry->keyHash = 0;
NS_IF_RELEASE(hashEntry->mDiskCacheBindData);
NS_IF_RELEASE(hashEntry->mBindData);
}
void PR_CALLBACK
nsDiskCacheHashTable::Finalize(PLDHashTable * table)
nsDiskCacheBindery::Finalize(PLDHashTable * table)
{
(void) PL_DHashTableEnumerate(table, FreeCacheEntries, nsnull);
}
PLDHashOperator PR_CALLBACK
nsDiskCacheHashTable::FreeCacheEntries(PLDHashTable * /* table */,
PLDHashEntryHdr * header,
PRUint32 number,
void * arg)
nsDiskCacheBindery::FreeCacheEntries(PLDHashTable * /* table */,
PLDHashEntryHdr * header,
PRUint32 number,
void * arg)
{
HashTableEntry *entry = (HashTableEntry *) header;
NS_IF_RELEASE(entry->mDiskCacheBindData);
NS_IF_RELEASE(entry->mBindData);
return PL_DHASH_NEXT;
}

View File

@@ -36,6 +36,8 @@
#include "nsITransport.h"
#endif
#include "nsDiskCacheMap.h"
/******************************************************************************
* nsDiskCacheBindData
@@ -50,19 +52,8 @@ class nsDiskCacheBindData : public nsISupports, public PRCList {
public:
NS_DECL_ISUPPORTS
nsDiskCacheBindData(nsCacheEntry* entry)
: mCacheEntry(entry),
mGeneration(0)
{
NS_INIT_ISUPPORTS();
PR_INIT_CLIST(this);
mHashNumber = Hash(entry->Key()->get());
}
virtual ~nsDiskCacheBindData()
{
PR_REMOVE_LINK(this);
}
nsDiskCacheBindData(nsCacheEntry* entry);
virtual ~nsDiskCacheBindData();
#ifdef MOZ_NEW_CACHE_REUSE_TRANSPORTS
/**
@@ -75,62 +66,65 @@ public:
return mTransports[mode - 1];
}
#endif
nsCacheEntry* getCacheEntry()
{
return mCacheEntry;
}
PRUint32 getGeneration()
{
return mGeneration;
}
void setGeneration(PRUint32 generation)
{
mGeneration = generation;
}
PLDHashNumber getHashNumber()
{
return mHashNumber;
}
nsrefcnt getRefCount()
{
return mRefCnt;
}
static PLDHashNumber Hash(const char* key);
// XXX make friends
public:
nsDiskCacheRecord mRecord;
PRInt32 mGeneration; // possibly just reservation
nsCacheEntry* mCacheEntry; // back pointer to parent nsCacheEntry
private:
#ifdef MOZ_NEW_CACHE_REUSE_TRANSPORTS
nsCOMPtr<nsITransport> mTransports[3];
#endif
nsCacheEntry* mCacheEntry; // back pointer to parent nsCacheEntry
PRUint32 mGeneration; // XXX part of nsDiskCacheRecord
PLDHashNumber mHashNumber; // XXX part of nsDiskCacheRecord
// XXX nsDiskCacheRecord mMapRecord;
};
/******************************************************************************
* nsDiskCacheHashTable
* Utility Functions
*****************************************************************************/
nsDiskCacheBindData * GetBindDataFromCacheEntry(nsCacheEntry * entry);
/******************************************************************************
* nsDiskCacheBindery
*
* Used to keep track of nsDiskCacheEntries associated with active/bound (and
* Used to keep track of nsDiskCacheBindData associated with active/bound (and
* possibly doomed) entries. Lookups on 4 byte disk hash to find collisions
* (which need to be doomed, instead of just evicted. Collisions are linked
* using a PRCList to keep track of current generation number.
*
* Used to detect hash number collisions, and find available generation numbers.
*
* Not all nsDiskCacheBindData have a generation number.
*
* Generation numbers may be aquired late, or lost (when data fits in block file)
*
* Collisions can occur:
* BindEntry() - hashnumbers collide (possibly different keys)
*
* Generation number required:
* DeactivateEntry() - metadata written to disk, may require file
* GetFileForEntry() - force data to require file
* writing to stream - data size may require file
*
* BindData can be kept in PRCList in order of generation numbers.
* BindData with no generation number can be Appended to PRCList (last).
*
*****************************************************************************/
class nsDiskCacheHashTable {
class nsDiskCacheBindery {
public:
nsDiskCacheHashTable();
~nsDiskCacheHashTable();
nsDiskCacheBindery();
~nsDiskCacheBindery();
nsresult Init();
nsDiskCacheBindData * CreateBindDataForCacheEntry(nsCacheEntry * entry);
nsDiskCacheBindData * GetEntry(const char * key);
nsDiskCacheBindData * GetEntry(PLDHashNumber key);
nsresult AddEntry(nsDiskCacheBindData * entry);
@@ -145,7 +139,7 @@ public:
private:
struct HashTableEntry : PLDHashEntryHdr {
nsDiskCacheBindData * mDiskCacheBindData; // STRONG ref?
nsDiskCacheBindData * mBindData;
};
// PLDHashTable operation callbacks

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,7 @@
#include "nsCacheDevice.h"
#include "nsDiskCacheBindData.h"
#include "nsDiskCacheBlockFile.h"
#include "nsDiskCacheEntry.h"
#include "nsILocalFile.h"
@@ -80,6 +81,19 @@ public:
PRUint32 getCacheSize();
PRUint32 getEntryCount();
private:
/**
* Private methods
*/
nsresult InitializeCacheDirectory();
nsresult GetCacheTrashDirectory(nsIFile ** result);
nsresult EvictDiskCacheEntries();
#if 0
// Old Code
nsresult getFileForHashNumber(PLDHashNumber hashNumber, PRBool meta, PRUint32 generation, nsIFile ** result);
nsresult getFileForKey(const char* key, PRBool meta, PRUint32 generation, nsIFile ** result);
nsresult getFileForDiskCacheEntry(nsDiskCacheBindData * bindData, PRBool meta, nsIFile ** result);
@@ -97,27 +111,26 @@ public:
nsresult deleteDiskCacheEntry(nsDiskCacheBindData * bindData);
nsresult scavengeDiskCacheEntries(nsDiskCacheBindData * bindData);
nsresult evictDiskCacheEntries();
nsresult InitializeCacheDirectory();
nsresult openCacheMap();
nsresult readCacheMap();
nsresult writeCacheMap();
nsresult updateCacheMap(nsDiskCacheBindData * bindData);
nsresult evictDiskCacheRecord(nsDiskCacheRecord * record);
#endif
private:
/**
* Member variables
*/
PRBool mInitialized;
nsCOMPtr<nsIObserver> mPrefsObserver; // XXX ?
nsCOMPtr<nsILocalFile> mCacheDirectory;
nsDiskCacheHashTable mBoundEntries; // XXX rename to refer to active entries
nsDiskCacheBindery mBindery;
PRUint32 mCacheCapacity; // XXX need soft/hard limits, currentTotal
nsDiskCacheMap* mCacheMap;
nsANSIFileStream* mCacheStream; // XXX should be owned by cache map
// XXX need array of cache block files
nsDiskCacheMap * mCacheMap;
};
#endif // _nsDiskCacheDevice_h_

View File

@@ -22,79 +22,121 @@
* Patrick C. Beard <beard@netscape.com>
*/
#include "nsDiskCache.h"
#include "nsDiskCacheEntry.h"
#include "nsDiskCacheBindData.h"
#include "nsDiskCacheMap.h"
#include "nsCache.h"
nsresult MetaDataFile::Read(nsIInputStream* input)
/******************************************************************************
* nsDiskCacheEntry
*****************************************************************************/
/**
* CreateCacheEntry()
*
* Creates an nsCacheEntry and sets all fields except for the bindData.
*/
nsCacheEntry *
nsDiskCacheEntry::CreateCacheEntry(nsCacheDevice * device)
{
nsresult rv;
PRUint32 count;
nsCacheEntry * entry = nsnull;
nsresult rv = nsCacheEntry::Create(mKeyStart,
nsICache::STREAM_BASED,
nsICache::STORE_ON_DISK,
device,
&entry);
if (NS_FAILED(rv) || !entry) return nsnull;
// XXX Is it less expensive to read the file in multiple parts, or
// XXX get the size and read it in one chunk?
// read in the file header.
rv = input->Read((char*)&mHeaderSize, sizeof(MetaDataHeader), &count);
if (NS_FAILED(rv)) return rv;
Unswap();
entry->SetFetchCount(mFetchCount);
entry->SetLastFetched(mLastFetched);
entry->SetLastModified(mLastModified);
entry->SetExpirationTime(mExpirationTime);
entry->SetCacheDevice(device);
// XXX why does nsCacheService have to fill out device in BindEntry()?
entry->SetDataSize(mDataSize);
// make sure it is self-consistent.
if (mHeaderSize != sizeof(MetaDataHeader)) {
NS_ERROR("### CACHE FORMAT CHANGED!!! PLEASE DELETE YOUR NewCache DIRECTORY!!! ###");
return NS_ERROR_ILLEGAL_VALUE;
}
// read in the key.
delete[] mKey;
mKey = new char[mKeySize];
if (!mKey) return NS_ERROR_OUT_OF_MEMORY;
rv = input->Read(mKey, mKeySize, &count);
if (NS_FAILED(rv)) return rv;
// read in the metadata.
delete mMetaData;
mMetaData = nsnull;
if (mMetaDataSize) {
mMetaData = new char[mMetaDataSize];
if (!mMetaData) return NS_ERROR_OUT_OF_MEMORY;
rv = input->Read(mMetaData, mMetaDataSize, &count);
if (NS_FAILED(rv)) return rv;
rv = entry->UnflattenMetaData(&mKeyStart[mKeySize], mMetaDataSize);
if (NS_FAILED(rv)) {
delete entry;
return nsnull;
}
return NS_OK;
return entry;
}
nsresult MetaDataFile::Write(nsIOutputStream* output)
/**
* CheckConsistency()
*
* Perform a few simple checks to verify the data looks reasonable.
*/
PRBool
nsDiskCacheEntry::CheckConsistency(PRUint32 size)
{
nsresult rv;
PRUint32 count;
if ((mHeaderVersion != nsDiskCache::kCurrentVersion) ||
(Size() > size) ||
(mKeySize == 0) ||
(mKeyStart[mKeySize - 1] != 0)) // key is null terminated
return PR_FALSE;
// write the header to the file.
Swap();
rv = output->Write((char*)&mHeaderSize, sizeof(MetaDataHeader), &count);
Unswap();
if (NS_FAILED(rv)) return rv;
// write the key to the file.
rv = output->Write(mKey, mKeySize, &count);
if (NS_FAILED(rv)) return rv;
// write the flattened metadata to the file.
if (mMetaDataSize) {
rv = output->Write(mMetaData, mMetaDataSize, &count);
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
return PR_TRUE;
}
/**
* CreateDiskCacheEntry(nsCacheEntry * entry)
*
* Prepare an nsCacheEntry for writing to disk
*/
nsDiskCacheEntry *
CreateDiskCacheEntry(nsDiskCacheBindData * bindData)
{
nsCacheEntry * entry = bindData->mCacheEntry;
if (!entry) return nsnull;
PRUint32 keySize = entry->Key()->Length() + 1;
PRUint32 size = sizeof(nsDiskCacheEntry) +
keySize + entry->MetaDataSize();
nsDiskCacheEntry * diskEntry = (nsDiskCacheEntry *)new char[size];
if (!diskEntry) return nsnull;
diskEntry->mHeaderVersion = nsDiskCache::kCurrentVersion;
diskEntry->mMetaLocation = bindData->mRecord.MetaLocation();
diskEntry->mFetchCount = entry->FetchCount();
diskEntry->mLastFetched = entry->LastFetched();
diskEntry->mLastModified = entry->LastModified();
diskEntry->mExpirationTime = entry->ExpirationTime();
diskEntry->mDataSize = entry->DataSize();
diskEntry->mKeySize = keySize;
diskEntry->mMetaDataSize = entry->MetaDataSize();
nsCRT::memcpy(diskEntry->mKeyStart, entry->Key()->get(),keySize);
char * metaData = nsnull;
PRUint32 metaSize = 0;
nsresult rv = entry->FlattenMetaData(&metaData, &metaSize);
diskEntry->mMetaDataSize = metaSize;
if (metaSize)
nsCRT::memcpy(&diskEntry->mKeyStart[keySize], metaData, metaSize);
return diskEntry;
}
/******************************************************************************
* nsDiskCacheEntryInfo
*****************************************************************************/
NS_IMPL_ISUPPORTS1(nsDiskCacheEntryInfo, nsICacheEntryInfo);
NS_IMETHODIMP nsDiskCacheEntryInfo::GetClientID(char ** clientID)
{
NS_ENSURE_ARG_POINTER(clientID);
return ClientIDFromCacheKey(nsLiteralCString(mMetaDataFile.mKey), clientID);
return ClientIDFromCacheKey(nsLiteralCString(mDiskEntry->mKeyStart), clientID);
}
extern const char DISK_CACHE_DEVICE_ID[];
@@ -109,30 +151,30 @@ NS_IMETHODIMP nsDiskCacheEntryInfo::GetDeviceID(char ** deviceID)
NS_IMETHODIMP nsDiskCacheEntryInfo::GetKey(char ** clientKey)
{
NS_ENSURE_ARG_POINTER(clientKey);
return ClientKeyFromCacheKey(nsLiteralCString(mMetaDataFile.mKey), clientKey);
return ClientKeyFromCacheKey(nsLiteralCString(mDiskEntry->mKeyStart), clientKey);
}
NS_IMETHODIMP nsDiskCacheEntryInfo::GetFetchCount(PRInt32 *aFetchCount)
{
return *aFetchCount = mMetaDataFile.mFetchCount;
return *aFetchCount = mDiskEntry->mFetchCount;
return NS_OK;
}
NS_IMETHODIMP nsDiskCacheEntryInfo::GetLastFetched(PRUint32 *aLastFetched)
{
*aLastFetched = mMetaDataFile.mLastFetched;
*aLastFetched = mDiskEntry->mLastFetched;
return NS_OK;
}
NS_IMETHODIMP nsDiskCacheEntryInfo::GetLastModified(PRUint32 *aLastModified)
{
*aLastModified = mMetaDataFile.mLastModified;
*aLastModified = mDiskEntry->mLastModified;
return NS_OK;
}
NS_IMETHODIMP nsDiskCacheEntryInfo::GetExpirationTime(PRUint32 *aExpirationTime)
{
*aExpirationTime = mMetaDataFile.mExpirationTime;
*aExpirationTime = mDiskEntry->mExpirationTime;
return NS_OK;
}
@@ -144,6 +186,6 @@ NS_IMETHODIMP nsDiskCacheEntryInfo::IsStreamBased(PRBool *aStreamBased)
NS_IMETHODIMP nsDiskCacheEntryInfo::GetDataSize(PRUint32 *aDataSize)
{
*aDataSize = mMetaDataFile.mDataSize;
*aDataSize = mDiskEntry->mDataSize;
return NS_OK;
}

View File

@@ -25,121 +25,73 @@
#ifndef _nsDiskCacheEntry_h_
#define _nsDiskCacheEntry_h_
#include "nsDiskCacheMap.h"
#include "nsCacheEntry.h"
#include "nsICacheVisitor.h"
#include "nspr.h"
#include "nscore.h"
#include "nsError.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsICacheVisitor.h"
#include "nsCacheEntry.h"
/******************************************************************************
* MetaData
* nsDiskCacheEntry
*****************************************************************************/
struct MetaDataHeader {
PRUint32 mHeaderSize;
// PRUint32 mHashNumber; // XXX
// PRUint32 mMetaLocation; // XXX
struct nsDiskCacheEntry {
PRUint32 mHeaderVersion; // useful for stand-alone metadata files
PRUint32 mMetaLocation; // for verification
PRInt32 mFetchCount;
PRUint32 mLastFetched;
PRUint32 mLastModified;
PRUint32 mExpirationTime;
PRUint32 mDataSize;
PRUint32 mKeySize;
PRUint32 mMetaDataSize;
// void * mKeyPtrSpace; // XXX don't need this
// void * mMetaDataPtrSpace; // XXX don't need this, we'll calculate it when necessary
// followed by null-terminated key and metadata string values.
PRUint32 mKeySize; // includes terminating null byte
PRUint32 mMetaDataSize; // includes terminating null byte
char mKeyStart[1]; // start of key data
// mMetaDataStart = mKeyStart[mKeySize];
MetaDataHeader()
: mHeaderSize(sizeof(MetaDataHeader)),
mFetchCount(0),
mLastFetched(0),
mLastModified(0),
mExpirationTime(0),
mDataSize(0),
mKeySize(0),
mMetaDataSize(0)
{
}
PRUint32 Size() { return sizeof(nsDiskCacheEntry) + mKeySize + mMetaDataSize; }
MetaDataHeader(nsCacheEntry* entry)
: mHeaderSize(sizeof(MetaDataHeader)),
mFetchCount(entry->FetchCount()),
mLastFetched(entry->LastFetched()),
mLastModified(entry->LastModified()),
mExpirationTime(entry->ExpirationTime()),
mDataSize(entry->DataSize()),
mKeySize(entry->Key()->Length() + 1),
mMetaDataSize(0)
nsCacheEntry * CreateCacheEntry(nsCacheDevice * device);
PRBool CheckConsistency(PRUint32 size);
void Swap() // host to network (memory to disk)
{
}
void Swap()
{
#if defined(IS_LITTLE_ENDIAN)
mHeaderSize = ::PR_htonl(mHeaderSize);
mFetchCount = ::PR_htonl(mFetchCount);
mLastFetched = ::PR_htonl(mLastFetched);
mLastModified = ::PR_htonl(mLastModified);
mExpirationTime = ::PR_htonl(mExpirationTime);
mDataSize = ::PR_htonl(mDataSize);
mKeySize = ::PR_htonl(mKeySize);
mMetaDataSize = ::PR_htonl(mMetaDataSize);
#if defined(IS_LITTLE_ENDIAN)
mHeaderVersion = ::PR_htonl(mHeaderVersion);
mMetaLocation = ::PR_htonl(mMetaLocation);
mFetchCount = ::PR_htonl(mFetchCount);
mLastFetched = ::PR_htonl(mLastFetched);
mLastModified = ::PR_htonl(mLastModified);
mExpirationTime = ::PR_htonl(mExpirationTime);
mDataSize = ::PR_htonl(mDataSize);
mKeySize = ::PR_htonl(mKeySize);
mMetaDataSize = ::PR_htonl(mMetaDataSize);
#endif
}
void Unswap()
void Unswap() // network to host (disk to memory)
{
#if defined(IS_LITTLE_ENDIAN)
mHeaderSize = ::PR_ntohl(mHeaderSize);
mFetchCount = ::PR_ntohl(mFetchCount);
mLastFetched = ::PR_ntohl(mLastFetched);
mLastModified = ::PR_ntohl(mLastModified);
mExpirationTime = ::PR_ntohl(mExpirationTime);
mDataSize = ::PR_ntohl(mDataSize);
mKeySize = ::PR_ntohl(mKeySize);
mMetaDataSize = ::PR_ntohl(mMetaDataSize);
mHeaderVersion = ::PR_ntohl(mHeaderSize);
mMetaLocation = ::PR_ntohl(mMetaLocation);
mFetchCount = ::PR_ntohl(mFetchCount);
mLastFetched = ::PR_ntohl(mLastFetched);
mLastModified = ::PR_ntohl(mLastModified);
mExpirationTime = ::PR_ntohl(mExpirationTime);
mDataSize = ::PR_ntohl(mDataSize);
mKeySize = ::PR_ntohl(mKeySize);
mMetaDataSize = ::PR_ntohl(mMetaDataSize);
#endif
}
};
struct MetaDataFile : MetaDataHeader {
char* mKey;
char* mMetaData;
nsDiskCacheEntry * CreateDiskCacheEntry(nsDiskCacheBindData * bindData);
MetaDataFile()
: mKey(nsnull), mMetaData(nsnull)
{
}
MetaDataFile(nsCacheEntry* entry)
: MetaDataHeader(entry),
mKey(nsnull), mMetaData(nsnull)
{
}
~MetaDataFile()
{
delete[] mKey;
delete[] mMetaData;
}
nsresult Init(nsCacheEntry* entry)
{
PRUint32 size = 1 + entry->Key()->Length();
mKey = new char[size];
if (!mKey) return NS_ERROR_OUT_OF_MEMORY;
nsCRT::memcpy(mKey, entry->Key()->get(), size);
return entry->FlattenMetaData(&mMetaData, &mMetaDataSize);
}
nsresult Read(nsIInputStream* input);
nsresult Write(nsIOutputStream* output);
};
/******************************************************************************
@@ -150,24 +102,20 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSICACHEENTRYINFO
nsDiskCacheEntryInfo(const char * deviceID)
nsDiskCacheEntryInfo(const char * deviceID, nsDiskCacheEntry * diskEntry)
: mDeviceID(deviceID)
, mDiskEntry(diskEntry)
{
NS_INIT_ISUPPORTS();
}
virtual ~nsDiskCacheEntryInfo() {}
nsresult Read(nsIInputStream * input)
{
return mMetaDataFile.Read(input);
}
const char* Key() { return mMetaDataFile.mKey; }
const char* Key() { return mDiskEntry->mKeyStart; }
private:
const char * mDeviceID;
MetaDataFile mMetaDataFile;
const char * mDeviceID;
nsDiskCacheEntry * mDiskEntry;
};

View File

@@ -23,94 +23,166 @@
*/
#include "nsDiskCacheMap.h"
#include "nsDiskCacheEntry.h"
#include "nsIFileStreams.h"
#include "nsCRT.h"
#include <string.h>
nsDiskCacheMap::nsDiskCacheMap()
: mStream(nsnull)
/******************************************************************************
* nsDiskCacheBucket
*****************************************************************************/
void
nsDiskCacheBucket::Swap()
{
nsDiskCacheRecord * record = &mRecords[0];
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (record->HashNumber() == 0)
break;
record->Swap();
}
}
void
nsDiskCacheBucket::Unswap()
{
nsDiskCacheRecord * record = &mRecords[0];
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (record->HashNumber() == 0)
break;
record->Unswap();
}
}
nsDiskCacheMap::~nsDiskCacheMap()
PRUint32
nsDiskCacheBucket::CountRecords()
{
}
nsresult
nsDiskCacheMap::Open(nsILocalFile * mapFile)
{
NS_ENSURE_ARG_POINTER(mapFile);
if (mStream) return NS_ERROR_ALREADY_INITIALIZED;
// create stream for cache map file
mStream = new nsANSIFileStream;
if (!mStream) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mStream);
if (mRecords[0].HashNumber() == 0) return 0;
// open the stream
nsresult rv = mStream->Open(mapFile);
if (NS_FAILED(rv)) {
NS_RELEASE(mStream);
mStream = nsnull;
return rv;
PRUint32 i = kRecordsPerBucket << 1;
PRUint32 offset = kRecordsPerBucket << 2;
while (offset > 0) {
if (mRecords[i].HashNumber()) i += offset;
else i -= offset;
offset <<= 1;
}
if (mRecords[i].HashNumber() != 0)
++i;
return i;
}
PRInt32
nsDiskCacheBucket::VisitEachRecordInBucket(nsDiskCacheRecordVisitor * visitor,
PRBool * dirty)
{
PRInt32 count = CountRecords();
*dirty = PR_FALSE;
if (count == 0) return kVisitNextRecord; // bucket is empty
// XXX call visitor for each entry
PRInt32 i = count - 1;
PRInt32 result = visitor->VisitRecord(&mRecords[i]);
return result;
}
/******************************************************************************
* nsDiskCacheMap
*****************************************************************************/
/**
* File operations
*/
nsresult
nsDiskCacheMap::Open(nsILocalFile * cacheDirectory)
{
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->Append("_CACHE_MAP_");
if (NS_FAILED(rv)) return rv;
// open the file
rv = localFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00666, &mMapFD);
if (NS_FAILED(rv)) return rv; // unable to open or create file
// check size of map file
PRUint32 mapSize;
rv = mStream->Available(&mapSize);
if (NS_FAILED(rv)) goto error_exit;
PRUint32 mapSize = PR_Available(mMapFD);
if (mapSize < 0) {
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
if (mapSize == 0) {
// create the file - initialize in memory
mHeader.mVersion = kCurrentVersion;
mHeader.mDataSize = kCacheMapSize;
mHeader.mVersion = nsDiskCache::kCurrentVersion;
mHeader.mDataSize = 0;
mHeader.mEntryCount = 0;
mHeader.mIsDirty = PR_TRUE;
nsCRT::zero(mBuckets, sizeof(nsDiskCacheBucket) * kBucketsPerTable);
mHeader.mIsDirty = PR_TRUE;
// XXX FlushCacheMap();
PRUint32 bytesWritten = 0;
mHeader.Swap();
rv = mStream->Write((char *)&mHeader, kCacheMapSize, &bytesWritten);
mHeader.Unswap();
if (NS_FAILED(rv)) goto error_exit;
if (kCacheMapSize != bytesWritten) {
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
nsCRT::zero(mHeader.reserved, nsDiskCacheHeader::kReservedBytes);
nsCRT::zero(mBuckets, sizeof(nsDiskCacheBucket) * kBucketsPerTable);
} else if (mapSize == kCacheMapSize) {
// read it in
PRUint32 bytesRead =0;
rv = mStream->Read((char *)&mHeader, kCacheMapSize, &bytesRead);
if (NS_FAILED(rv)) goto error_exit;
PRUint32 bytesRead = PR_Read(mMapFD, &mHeader, kCacheMapSize);
if (kCacheMapSize != bytesRead) {
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
mHeader.Unswap();
if (IsDirty() || mHeader.mVersion != kCurrentVersion) {
if (mHeader.mIsDirty || mHeader.mVersion != nsDiskCache::kCurrentVersion) {
rv = NS_ERROR_FILE_CORRUPTED;
goto error_exit;
}
// XXX Unswap each bucket
// Unswap each bucket
for (PRUint32 i = 0; i < kBucketsPerTable; ++i) {
mBuckets[i].Unswap();
}
// XXX verify entry count, check size(?)
} else {
rv = NS_ERROR_FILE_CORRUPTED;
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:
if (mStream) {
(void) mStream->Close();
NS_RELEASE(mStream);
mStream = nsnull;
// XXX close block files
if (mMapFD) {
(void) PR_Close(mMapFD);
mMapFD = nsnull;
}
return rv;
@@ -120,38 +192,138 @@ error_exit:
nsresult
nsDiskCacheMap::Close()
{
if (!mStream) return NS_OK;
if (!mMapFD) return NS_OK;
// close block files
for (int i = 0; i < 3; ++i) {
(void) mBlockFile[i].Close(); // XXX report rv
}
// write map record buckets
nsresult rv = FlushBuckets(PR_FALSE); // don't bother swapping buckets back
if (NS_FAILED(rv)) goto exit;
// XXX nsresult rv = FlushCacheMap();
// clear dirty bit
mHeader.mIsDirty = PR_FALSE;
rv = FlushHeader();
exit:
PRStatus err = PR_Close(mMapFD);
mMapFD = nsnull;
nsresult rv2 = mStream->Close();
NS_RELEASE(mStream);
mStream = nsnull;
// XXX dealloc cache map memory?
// return rv ? rv : rv2;
return rv2;
if (NS_FAILED(rv)) return rv;
return err == PR_SUCCESS ? NS_OK : NS_ERROR_UNEXPECTED;
}
nsresult
nsDiskCacheMap::FlushHeader()
{
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;
}
nsresult
nsDiskCacheMap::FlushBuckets(PRBool unswap)
{
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;
// Swap each bucket
for (PRUint32 i = 0; i < kBucketsPerTable; ++i) {
mBuckets[i].Swap();
}
PRInt32 bytesWritten = PR_Write(mMapFD, &mBuckets, sizeof(nsDiskCacheBucket) * kBucketsPerTable);
if (unswap) {
// Unswap each bucket
for (PRUint32 i = 0; i < kBucketsPerTable; ++i) {
mBuckets[i].Unswap();
}
}
if (sizeof(nsDiskCacheHeader) != bytesWritten) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
/**
* Record operations
*/
nsresult
nsDiskCacheMap::AddRecord( nsDiskCacheRecord * mapRecord,
nsDiskCacheRecord * oldRecord)
{
PRUint32 hashNumber = mapRecord->HashNumber();
nsDiskCacheBucket * bucket;
nsresult rv = GetBucketForHashNumber(hashNumber, &bucket);
if (NS_FAILED(rv)) return rv;
nsDiskCacheRecord * mostEvictable = &bucket->mRecords[0];
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (bucket->mRecords[i].HashNumber() == 0) {
// stick the new record here
bucket->mRecords[i] = *mapRecord;
oldRecord->SetHashNumber(0); // signify no record
return NS_OK;
}
if (bucket->mRecords[i].EvictionRank() > mostEvictable->EvictionRank())
mostEvictable = &bucket->mRecords[i];
}
*oldRecord = *mostEvictable; // i == kRecordsPerBucket, so evict the mostEvictable
*mostEvictable = *mapRecord; // replace it with the new record
// XXX recalc mostEvictable
return NS_OK;
}
nsresult
nsDiskCacheMap::UpdateRecord( nsDiskCacheRecord * mapRecord)
{
PRUint32 hashNumber = mapRecord->HashNumber();
nsDiskCacheBucket * bucket;
nsresult rv = GetBucketForHashNumber(hashNumber, &bucket);
if (NS_FAILED(rv)) return rv;
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (bucket->mRecords[i].HashNumber() == mapRecord->HashNumber()) {
// stick the new record here
bucket->mRecords[i] = *mapRecord;
return NS_OK;
}
}
return NS_ERROR_UNEXPECTED;
}
nsresult
nsDiskCacheMap::FindRecord( PRUint32 hashNumber, nsDiskCacheRecord * mapRecord)
nsDiskCacheMap::FindRecord( PRUint32 hashNumber, nsDiskCacheRecord * result)
{
nsDiskCacheBucket * bucket;
nsresult rv = GetBucketForHashNumber(hashNumber, &bucket);
@@ -159,73 +331,259 @@ nsDiskCacheMap::FindRecord( PRUint32 hashNumber, nsDiskCacheRecord * mapRecord
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (bucket->mRecords[i].HashNumber() == 0) break;
if (bucket->mRecords[i].HashNumber() == mapRecord->HashNumber()) {
*mapRecord = bucket->mRecords[i]; // copy the record
if (bucket->mRecords[i].HashNumber() == hashNumber) {
*result = bucket->mRecords[i]; // copy the record
return NS_OK;
}
}
// XXX ? mapRecord->hashNumber == 0;
return NS_OK;
return NS_ERROR_CACHE_KEY_NOT_FOUND;
}
nsresult
nsDiskCacheMap::DeleteRecord2( nsDiskCacheRecord * mapRecord)
nsDiskCacheMap::DeleteRecord( nsDiskCacheRecord * mapRecord)
{
nsDiskCacheBucket * bucket;
nsresult rv = GetBucketForHashNumber(mapRecord->HashNumber(), &bucket);
if (NS_FAILED(rv)) return rv;
for (int i = 0; i < kRecordsPerBucket; ++i) {
if (bucket->mRecords[i].HashNumber() == 0) {
mapRecord->SetHashNumber(0);
return NS_ERROR_UNEXPECTED;
}
PRUint32 count = bucket->CountRecords();
for (int i = 0; i < count; ++i) {
if (bucket->mRecords[i].HashNumber() == mapRecord->HashNumber()) {
// found it, now delete it.
if (i != (count - 1)) { // if not the last record, shift last record into opening
bucket->mRecords[i] = bucket->mRecords[count - 1];
}
bucket->mRecords[count - 1].SetHashNumber(0); // clear last record
return NS_OK;
}
}
// XXX ? mapRecord->hashNumber == 0;
return NS_ERROR_UNEXPECTED;
}
nsresult
nsDiskCacheMap::EvictRecords( nsDiskCacheRecordVisitor * visitor)
nsDiskCacheMap::VisitRecords( nsDiskCacheRecordVisitor * visitor)
{
}
/******************************************************************************
* Private methods
*****************************************************************************/
void nsDiskCacheMap::Reset()
{
mHeader.mDataSize = 0;
mHeader.mEntryCount = 0;
mHeader.mIsDirty = PR_TRUE;
for (PRUint32 b = 0; b < kBucketsPerTable; ++b) {
nsDiskCacheBucket * bucket = &mBuckets[b];
::memset(bucket, 0, sizeof(nsDiskCacheBucket));
for (PRUint32 i = 0; i < kBucketsPerTable; ++i) {
// get bucket
PRBool dirty;
PRBool continueFlag = mBuckets[i].VisitEachRecordInBucket(visitor, &dirty);
if (dirty) {
// XXX write bucket
}
}
return NS_OK;
}
nsresult
nsDiskCacheMap::GetBucketForHashNumber(PRUint32 hashNumber, nsDiskCacheBucket ** result)
nsDiskCacheMap::OpenBlockFiles()
{
*result = &mBuckets[GetBucketIndex(hashNumber)];
// 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);
rv = mBlockFile[i].Open(blockFile, blockSize);
if (NS_FAILED(rv)) goto error_exit;
}
return NS_OK;
error_exit:
(void)CloseBlockFiles(); // we already have an error to report
return rv;
}
nsresult
nsDiskCacheMap::CloseBlockFiles()
{
nsresult rv, rv2 = NS_OK;
for (int i=0; i < 3; ++i) {
rv = mBlockFile[i].Close();
if (NS_FAILED(rv)) rv2 = rv; // if one or more errors, report at least one
}
return rv2;
}
nsresult
nsDiskCacheMap::ReadDiskCacheEntry(nsDiskCacheRecord * record, nsDiskCacheEntry ** result)
{
nsresult rv;
nsDiskCacheEntry * diskEntry = nsnull;
PRUint16 generation = record->Generation();
PRUint32 hashNumber = record->HashNumber();
PRUint32 metaFile = record->MetaFile();
PRFileDesc * fd = nsnull;
*result = nsnull;
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;
nsresult rv = file->OpenNSPRFileDesc(PR_RDONLY, 00666, &fd);
if (NS_FAILED(rv)) return rv;
PRInt32 fileSize = PR_Available(fd);
if (fileSize < 0) {
// XXX 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 - 1);
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;
}
// pass ownership to caller
*result = diskEntry;
diskEntry = nsnull;
exit:
// XXX auto ptr would be nice
if (fd) (void) PR_Close(fd);
delete diskEntry;
return rv;
}
nsresult
nsDiskCacheMap::WriteDiskCacheEntry(nsDiskCacheEntry * diskEntry,
nsDiskCacheBindData * bindData)
{
nsresult rv = NS_OK;
if (diskEntry->Size() < 1024) { // block size 256
} else if (diskEntry->Size() < 4096) { // block size 1024
} else if (diskEntry->Size() < 16384) { // block size 4096
} else { // separate file
}
// XXX if block file != reallocate
// XXX if block size != reallocate
// XXX deallocate previously used blocks
// XXX allocate new blocks
// XXX update bindData so caller can update cache map
// XXX write data
return NS_OK;
}
nsresult
nsDiskCacheMap::GetFileForDiskCacheRecord(nsDiskCacheRecord * record,
PRBool meta,
nsIFile ** result)
{
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->Append(name);
if (NS_FAILED(rv)) return rv;
return rv;
}
nsresult
nsDiskCacheMap::GetLocalFileForDiskCacheRecord(nsDiskCacheRecord * record,
PRBool meta,
nsILocalFile ** result)
{
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;
}
nsresult
nsDiskCacheMap::GetBlockFileForIndex(PRUint32 index, nsILocalFile ** result)
{
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->Append(name);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
NS_IF_ADDREF(*result = localFile);
return rv;
}
PRUint32
nsDiskCacheMap::GetBlockSizeForIndex(PRUint32 index)
{
return 256 << (2 * (index)); // XXX magic numbers
}
#if 0
/******************************************************************************
* old code
*****************************************************************************/
#ifdef XP_MAC
#pragma mark -
#pragma mark OLD CODE
#endif
nsDiskCacheRecord* nsDiskCacheMap::GetRecord(PRUint32 hashNumber)
{
nsDiskCacheBucket& bucket = mBuckets[GetBucketIndex(hashNumber)];
@@ -242,7 +600,6 @@ nsDiskCacheRecord* nsDiskCacheMap::GetRecord(PRUint32 hashNumber)
return oldestRecord;
}
void nsDiskCacheMap::DeleteRecord(nsDiskCacheRecord* deletedRecord)
{
PRUint32 hashNumber = deletedRecord->HashNumber();
@@ -271,7 +628,6 @@ void nsDiskCacheMap::DeleteRecord(nsDiskCacheRecord* deletedRecord)
mHeader.mEntryCount--;
}
nsresult nsDiskCacheMap::Read(nsIInputStream* input)
{
nsresult rv;
@@ -421,24 +777,5 @@ nsresult nsDiskCacheMap::WriteBucket(nsIOutputStream* output, PRUint32 index)
return rv;
}
nsresult nsDiskCacheMap::WriteHeader(nsIOutputStream* output)
{
nsresult rv;
PRUint32 count;
// can only do this if the stream is seekable.
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(output, &rv);
if (NS_FAILED(rv)) return rv;
#endif
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
if (NS_FAILED(rv)) return rv;
// write the header.
nsDiskCacheHeader header = mHeader;
header.Swap();
rv = output->Write((char*)&header, sizeof(header), &count);
output->Flush();
if (count != sizeof(header)) return NS_ERROR_FAILURE;
return rv;
}

View File

@@ -30,17 +30,24 @@
#include "nsDebug.h"
#include "nsError.h"
#include "nsILocalFile.h"
#include "nsANSIFileStreams.h"
class nsIInputStream;
class nsIOutputStream;
#include "nsDiskCache.h"
#include "nsDiskCacheBlockFile.h"
class nsDiskCacheBindData;
class 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
* 1100 1100 0000 0000 0000 0000 0000 0000 : reserved bits
* 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 : eFileReservedMask
@@ -51,107 +58,171 @@ class nsIOutputStream;
* 1 = 256 byte block file
* 2 = 1k block file
* 3 = 4k block file
*/
/******************************************************************************
* nsDiskCacheRecord
*
*****************************************************************************/
class nsDiskCacheRecord {
private:
PRUint32 mHashNumber;
PRUint32 mEvictionRank;
PRUint32 mLocation;
PRUint32 mDataLocation;
PRUint32 mMetaLocation;
enum {
eLocationSelectorMask = 0x30000000,
eLocationSelectorOffset = 28,
eLocationInitializedMask = 0x80000000,
eExtraBlocksMask = 0x03000000,
eExtraBlocksOffset = 24,
eLocationSelectorMask = 0x30000000,
eLocationSelectorOffset = 28,
eReservedMask = 0xCC000000,
eExtraBlocksMask = 0x03000000,
eExtraBlocksOffset = 24,
eBlockNumberMask = 0x00FFFFFF,
eReservedMask = 0xCC000000,
eBlockNumberMask = 0x00FFFFFF,
eFileReservedMask = 0x00FFFF00,
eFileGenerationMask = 0x000000FF
eFileReservedMask = 0x00FFFF00,
eFileGenerationMask = 0x000000FF
};
public:
nsDiskCacheRecord()
: mHashNumber(0), mEvictionRank(0), mLocation(0), mMetaLocation(0)
: mHashNumber(0), mEvictionRank(0), mDataLocation(0), mMetaLocation(0)
{
}
PRUint32 HashNumber() const
{
return mHashNumber;
}
// HashNumber accessors
PRUint32 HashNumber() const { return mHashNumber; }
void SetHashNumber(PRUint32 hashNumber) { mHashNumber = hashNumber; }
// EvictionRank accessors
PRUint32 EvictionRank() const { return mEvictionRank; }
void SetEvictionRank(PRUint32 rank) { mEvictionRank = rank; }
// DataLocation accessors
PRBool DataLocationInitialized() { return mDataLocation & eLocationInitializedMask; }
void SetHashNumber(PRUint32 hashNumber)
PRUint32 DataLocation() { return mDataLocation; }
void SetDataLocation(PRUint32 location) { mDataLocation = location; }
PRUint32 DataFile() const
{
mHashNumber = hashNumber;
return (PRUint32)(mDataLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
}
PRUint32 EvictionRank() const
void SetDataBlocks(PRUint32 index, PRUint32 startBlock, PRUint32 blockCount)
{
return mEvictionRank;
// clear everything
mDataLocation = 0;
// set file index
NS_ASSERTION( index < 4,"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 = --blockCount;
mDataLocation |= (blockCount & eExtraBlocksMask) << eExtraBlocksOffset;
mDataLocation |= eLocationInitializedMask;
}
void SetEvictionRank(PRUint32 rank)
PRUint32 DataBlockCount() const
{
mEvictionRank = rank;
return (PRUint32)((mDataLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
}
PRUint32 LocationSelector() const
PRUint32 DataStartBlock() const
{
return (PRUint32)(mLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
return (mDataLocation & eBlockNumberMask);
}
void SetLocationSelector(PRUint32 selector)
PRUint16 DataGeneration() const
{
mLocation &= ~eLocationSelectorMask; // clear location selector bits
mLocation |= (selector & eLocationSelectorMask) << eLocationSelectorOffset;
return (mDataLocation & eFileGenerationMask);
}
PRUint32 BlockCount() const
void SetDataFileGeneration(PRUint16 generation)
{
return (PRUint32)((mLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
// clear everything, (separate file index = 0)
mDataLocation = 0;
mDataLocation |= generation & eFileGenerationMask;
mDataLocation |= eLocationInitializedMask;
}
void SetBlockCount(PRUint32 count)
// MetaLocation accessors
PRBool MetaLocationInitialized() { return mMetaLocation & eLocationInitializedMask; }
PRUint32 MetaLocation() { return mMetaLocation; }
void SetMetaLocation(PRUint32 location) { mMetaLocation = location; }
PRUint32 MetaFile() const
{
NS_ASSERTION( (count>=1) && (count<=4),"invalid block count");
count = --count;
mLocation &= ~eExtraBlocksMask; // clear extra blocks bits
mLocation |= (count & eExtraBlocksMask) << eExtraBlocksOffset;
return (PRUint32)(mMetaLocation & eLocationSelectorMask) >> eLocationSelectorOffset;
}
PRUint32 BlockNumber() const
void SetMetaBlocks(PRUint32 index, PRUint32 startBlock, PRUint32 blockCount)
{
return (mLocation & eBlockNumberMask);
// clear everything
mMetaLocation = 0;
// set file index
NS_ASSERTION( index < 4, "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 = --blockCount;
mMetaLocation |= (blockCount & eExtraBlocksMask) << eExtraBlocksOffset;
mMetaLocation |= eLocationInitializedMask;
}
void SetBlockNumber(PRUint32 blockNumber)
PRUint32 MetaBlockCount() const
{
mLocation &= ~eBlockNumberMask; // clear block number bits
mLocation |= blockNumber & eBlockNumberMask;
return (PRUint32)((mMetaLocation & eExtraBlocksMask) >> eExtraBlocksOffset) + 1;
}
PRUint16 FileGeneration() const
PRUint32 MetaStartBlock() const
{
return (mLocation & eFileGenerationMask);
return (mMetaLocation & eBlockNumberMask);
}
void SetFileGeneration(PRUint16 generation)
PRUint16 MetaGeneration() const
{
mLocation &= ~eFileGenerationMask; // clear file generation bits
mLocation |= generation & eFileGenerationMask;
return (mMetaLocation & eFileGenerationMask);
}
void SetMetaFileGeneration(PRUint16 generation)
{
// clear everything, (separate file index = 0)
mMetaLocation = 0;
mMetaLocation |= generation & eFileGenerationMask;
mMetaLocation |= eLocationInitializedMask;
}
PRUint16 Generation() const
{
if (DataFile() == 0) return DataGeneration();
else if (MetaFile() == 0) return MetaGeneration();
return -1; // no generation
}
void Swap()
{
#if defined(IS_LITTLE_ENDIAN)
@@ -185,7 +256,9 @@ enum { kDeleteRecordAndContinue = -1,
};
class nsDiskCacheRecordVisitor {
PRInt32 VisitRecord(nsDiskCacheRecord * mapRecord);
public:
virtual PRInt32 VisitRecord(nsDiskCacheRecord * mapRecord) = 0;
};
@@ -198,27 +271,33 @@ enum {
struct nsDiskCacheBucket {
nsDiskCacheRecord mRecords[kRecordsPerBucket];
void Swap();
void Unswap();
PRUint32 CountRecords();
PRInt32 VisitEachRecordInBucket(nsDiskCacheRecordVisitor * visitor,
PRBool * dirty);
};
/******************************************************************************
* nsDiskCacheHeader
*****************************************************************************/
enum { kCurrentVersion = 0x00010002 };
struct nsDiskCacheHeader {
PRUint32 mVersion; // cache version.
PRUint32 mDataSize; // size of cache in bytes.
PRUint32 mEntryCount; // number of entries stored in cache.
PRInt32 mDataSize; // size of cache in bytes.
PRInt32 mEntryCount; // number of entries stored in cache.
PRUint32 mIsDirty; // dirty flag.
// pad to blocksize
PRUint8 reserved[sizeof(nsDiskCacheBucket) - 4 * sizeof(PRUint32)];
enum { kReservedBytes = sizeof(nsDiskCacheBucket) - 4 * sizeof(PRUint32) };
PRUint8 reserved[kReservedBytes];
// XXX need a bitmap?
nsDiskCacheHeader()
: mVersion(kCurrentVersion), mDataSize(0),
: mVersion(nsDiskCache::kCurrentVersion), mDataSize(0),
mEntryCount(0), mIsDirty(PR_TRUE)
{
}
@@ -247,10 +326,10 @@ struct nsDiskCacheHeader {
/******************************************************************************
* nsDiskCacheMap
*
* // XXX initial capacity, enough for 8192 distinct entries.
*****************************************************************************/
// XXX initial capacity, enough for 8192 distinct entries.
enum {
kBucketsPerTable = (1 << 5), // must be a power of 2!
kCacheMapSize = sizeof(nsDiskCacheHeader) +
@@ -260,72 +339,105 @@ enum {
class nsDiskCacheMap {
public:
nsDiskCacheMap();
~nsDiskCacheMap();
nsDiskCacheMap()
: mCacheDirectory(nsnull)
, mMapFD(nsnull)
{}
~nsDiskCacheMap() { (void) Close(); }
/**
* 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);
nsresult Open(nsILocalFile * mapFile);
nsresult Close();
nsresult Open(nsILocalFile * cacheDirectory);
nsresult Close();
// nsresult Flush();
// nsresult Flush();
nsresult FlushHeader();
nsresult FlushBuckets(PRBool unswap);
/**
* Record operations
*
* AddRecord -
*/
nsresult AddRecord( nsDiskCacheRecord * mapRecord, nsDiskCacheRecord * oldRecord);
nsresult UpdateRecord( nsDiskCacheRecord * mapRecord);
nsresult FindRecord( PRUint32 hashNumber, nsDiskCacheRecord * mapRecord);
nsresult DeleteRecord2( nsDiskCacheRecord * mapRecord);
nsresult EvictRecords( nsDiskCacheRecordVisitor * visitor);
nsresult DeleteRecord( nsDiskCacheRecord * mapRecord);
nsresult VisitRecords( nsDiskCacheRecordVisitor * visitor);
//private:
void Reset();
/**
* Disk Entry operations
*/
nsresult OpenBlockFiles();
nsresult CloseBlockFiles();
PRUint32& DataSize() { return mHeader.mDataSize; }
PRUint32& EntryCount() { return mHeader.mEntryCount; }
PRUint32& IsDirty() { return mHeader.mIsDirty; }
nsDiskCacheRecord* GetRecord(PRUint32 hashNumber);
void DeleteRecord(nsDiskCacheRecord* record);
nsresult GetBucketForHashNumber(PRUint32 hashNumber, nsDiskCacheBucket ** result);
nsresult GetFileForDiskCacheRecord(nsDiskCacheRecord * record,
PRBool meta,
nsIFile ** result);
nsresult GetLocalFileForDiskCacheRecord(nsDiskCacheRecord * record,
PRBool meta,
nsILocalFile ** result);
nsDiskCacheRecord* GetBucket(PRUint32 index)
nsresult GetBlockFileForIndex(PRUint32 index, nsILocalFile ** result);
PRUint32 GetBlockSizeForIndex(PRUint32 index);
nsresult ReadDiskCacheEntry( nsDiskCacheRecord * record, nsDiskCacheEntry ** result);
nsresult WriteDiskCacheEntry( nsDiskCacheEntry * diskEntry,
nsDiskCacheBindData * bindData);
/**
* Statistical Operations
*/
void IncrementTotalSize(PRInt32 delta)
{
mHeader.mDataSize += delta;
mHeader.mIsDirty = PR_TRUE;
}
void DecrementTotalSize(PRInt32 delta)
{
mHeader.mDataSize -= delta;
mHeader.mIsDirty = PR_TRUE;
}
PRInt32 TotalSize() { return mHeader.mDataSize; }
PRInt32 EntryCount() { return mHeader.mEntryCount; }
private:
/**
* Private methods
*/
nsresult GetBucketForHashNumber(PRUint32 hashNumber, nsDiskCacheBucket ** result)
{
return mBuckets[index].mRecords;
*result = &mBuckets[GetBucketIndex(hashNumber)];
return NS_OK;
}
PRUint32 GetBucketIndex(PRUint32 hashNumber)
{
return (hashNumber & (kBucketsPerTable - 1));
}
PRUint32 GetBucketIndex(nsDiskCacheRecord* record)
{
return GetBucketIndex(record->HashNumber());
}
nsresult Read(nsIInputStream* input);
nsresult Write(nsIOutputStream* output);
nsresult ReadBucket(nsIInputStream* input, PRUint32 index);
nsresult WriteBucket(nsIOutputStream* output, PRUint32 index);
nsresult ReadHeader(nsIInputStream* input);
nsresult WriteHeader(nsIOutputStream* output);
/**
* data members
*/
private:
nsANSIFileStream * mStream;
nsCOMPtr<nsILocalFile> mCacheDirectory;
PRFileDesc * mMapFD;
nsDiskCacheBlockFile mBlockFile[3];
nsDiskCacheHeader mHeader;
nsDiskCacheBucket mBuckets[kBucketsPerTable];
};