diff --git a/mozilla/netwerk/cache/src/nsCacheEntry.cpp b/mozilla/netwerk/cache/src/nsCacheEntry.cpp index 1a6110c7095..524e5ec8384 100644 --- a/mozilla/netwerk/cache/src/nsCacheEntry.cpp +++ b/mozilla/netwerk/cache/src/nsCacheEntry.cpp @@ -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; diff --git a/mozilla/netwerk/cache/src/nsCacheEntry.h b/mozilla/netwerk/cache/src/nsCacheEntry.h index 3d0b274416e..efa558217af 100644 --- a/mozilla/netwerk/cache/src/nsCacheEntry.h +++ b/mozilla/netwerk/cache/src/nsCacheEntry.h @@ -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; } diff --git a/mozilla/netwerk/cache/src/nsDiskCache.h b/mozilla/netwerk/cache/src/nsDiskCache.h index 1a2bb5788b6..6b44595ecbe 100644 --- a/mozilla/netwerk/cache/src/nsDiskCache.h +++ b/mozilla/netwerk/cache/src/nsDiskCache.h @@ -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_ diff --git a/mozilla/netwerk/cache/src/nsDiskCacheBindData.cpp b/mozilla/netwerk/cache/src/nsDiskCacheBindData.cpp index f8126c613b9..5040b322a68 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheBindData.cpp +++ b/mozilla/netwerk/cache/src/nsDiskCacheBindData.cpp @@ -26,27 +26,51 @@ #include "nsDiskCacheBindData.h" + +/****************************************************************************** + * Utility Functions + *****************************************************************************/ + +nsDiskCacheBindData * +GetBindDataFromCacheEntry(nsCacheEntry * entry) +{ + nsCOMPtr 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 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; } diff --git a/mozilla/netwerk/cache/src/nsDiskCacheBindData.h b/mozilla/netwerk/cache/src/nsDiskCacheBindData.h index 46114461df6..cb6c6c11085 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheBindData.h +++ b/mozilla/netwerk/cache/src/nsDiskCacheBindData.h @@ -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 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 diff --git a/mozilla/netwerk/cache/src/nsDiskCacheDevice.cpp b/mozilla/netwerk/cache/src/nsDiskCacheDevice.cpp index 2e0b4990ab3..7dc98727861 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheDevice.cpp +++ b/mozilla/netwerk/cache/src/nsDiskCacheDevice.cpp @@ -24,10 +24,11 @@ #include -#include "nsANSIFileStreams.h" #include "nsDiskCacheDevice.h" #include "nsDiskCacheEntry.h" #include "nsDiskCacheMap.h" +#include "nsDiskCache.h" + #include "nsCacheService.h" #include "nsCache.h" @@ -256,26 +257,14 @@ static nsresult removeObservers(nsDiskCacheDevice* device) } -/****************************************************************************** - * static utility functions - *****************************************************************************/ -static nsDiskCacheBindData* -ensureDiskCacheBindData(nsCacheEntry * entry) -{ - nsCOMPtr data; - nsresult rv = entry->GetData(getter_AddRefs(data)); - if (NS_SUCCEEDED(rv) && !data) { - nsDiskCacheBindData* bindData = new nsDiskCacheBindData(entry); - data = bindData; - if (NS_SUCCEEDED(rv) && data) - entry->SetData(data.get()); - } - return (nsDiskCacheBindData*) (nsISupports*) data.get(); -} - /****************************************************************************** * nsDiskCacheDeviceInfo *****************************************************************************/ +#ifdef XP_MAC +#pragma mark - +#pragma mark DISK CACHE DEVICE INFO +#endif + class nsDiskCacheDeviceInfo : public nsICacheDeviceInfo { public: NS_DECL_ISUPPORTS @@ -292,6 +281,7 @@ public: private: nsDiskCacheDevice* mDevice; }; + NS_IMPL_ISUPPORTS1(nsDiskCacheDeviceInfo, nsICacheDeviceInfo); /* readonly attribute string description; */ @@ -321,7 +311,7 @@ NS_IMETHODIMP nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport) buffer.Append("directory unavailable"); } buffer.Append(""); - buffer.Append("Files: 1492"); + buffer.Append("Files: XXX"); buffer.Append(""); *usageReport = buffer.ToNewCString(); if (!*usageReport) return NS_ERROR_OUT_OF_MEMORY; @@ -354,13 +344,40 @@ NS_IMETHODIMP nsDiskCacheDeviceInfo::GetMaximumSize(PRUint32 *aMaximumSize) } +/****************************************************************************** + * nsDiskCache + *****************************************************************************/ + +/** + * nsDiskCache::Hash(const char * key) + * + * This algorithm of this method implies nsDiskCacheRecords will be stored + * in a certain order on disk. If the algorithm changes, existing cache + * map files may become invalid, and therefore the kCurrentVersion needs + * to be revised. + */ +PLDHashNumber +nsDiskCache::Hash(const char * key) +{ + 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); +} + + /****************************************************************************** * nsDiskCacheDevice *****************************************************************************/ static nsCOMPtr gFileTransportService; +#ifdef XP_MAC +#pragma mark - +#pragma mark DISK CACHE DEVICE +#endif + nsDiskCacheDevice::nsDiskCacheDevice() - : mInitialized(PR_FALSE), mCacheCapacity(0), mCacheMap(nsnull), mCacheStream(nsnull) + : mInitialized(PR_FALSE), mCacheCapacity(0), mCacheMap(nsnull) { } @@ -368,7 +385,6 @@ nsDiskCacheDevice::~nsDiskCacheDevice() { Shutdown(); delete mCacheMap; - NS_IF_RELEASE(mCacheStream); } @@ -380,63 +396,65 @@ nsDiskCacheDevice::Init() { nsresult rv; - rv = mBoundEntries.Init(); + rv = mBindery.Init(); if (NS_FAILED(rv)) return rv; - mCacheMap = new nsDiskCacheMap; - if (!mCacheMap) return NS_ERROR_OUT_OF_MEMORY; - // XXX examine preferences, and install observers to react to changes. rv = installObservers(this); if (NS_FAILED(rv)) return rv; - // XXX cache the file transport service to avoid repeated round trips - // through the service manager. + // hold the file transport service to avoid excessive calls to the service manager. gFileTransportService = do_GetService("@mozilla.org/network/file-transport-service;1", &rv); if (NS_FAILED(rv)) return rv; - + + // XXX are we sure we want to do this on startup? // delete "Cache.Trash" folder nsCOMPtr cacheTrashDir; - rv = mCacheDirectory->Clone(getter_AddRefs(cacheTrashDir)); + rv = GetCacheTrashDirectory(getter_AddRefs(cacheTrashDir)); if (NS_FAILED(rv)) return rv; - rv = cacheTrashDir->SetLeafName("Cache.Trash"); - if (NS_FAILED(rv)) return rv; - (void) cacheTrashDir->Delete(PR_TRUE); // ignore errors, we tried... + (void) cacheTrashDir->Delete(PR_TRUE); // ignore errors, we tried... - // XXX read in persistent information about the cache. this can fail, if - // no cache directory has ever existed before. - rv = readCacheMap(); - if (NS_FAILED(rv) || mCacheMap->IsDirty()) { - rv = InitializeCacheDirectory(); // XXX initializeCacheDirectory() + // Try opening cache map file. + mCacheMap = new nsDiskCacheMap; + if (!mCacheMap) return NS_ERROR_OUT_OF_MEMORY; + + rv = mCacheMap->Open(mCacheDirectory); + if (NS_FAILED(rv)) { + rv = InitializeCacheDirectory(); // retry one time if (NS_FAILED(rv)) return rv; } - // record that initialization succeeded. - mInitialized = PR_TRUE; + mInitialized = PR_TRUE; // record that initialization succeeded. return NS_OK; + +error_exit: + // XXX de-install observers? + + (void) mCacheMap->Close(); + gFileTransportService = nsnull; + + return rv; } + nsresult nsDiskCacheDevice::Shutdown() { if (mInitialized) { - // XXX implement poor man's eviction strategy right here, - // keep deleting cache entries from oldest to newest, until - // cache usage is brought below limits. - evictDiskCacheEntries(); + // check cache limits in case we need to evict. + EvictDiskCacheEntries(); - // XXX write out persistent information about the cache. - mCacheMap->IsDirty() = PR_FALSE; - writeCacheMap(); + // write out persistent information about the cache. + (void) mCacheMap->Close(); - // XXX no longer initialized. + // no longer initialized. mInitialized = PR_FALSE; } - // XXX disconnect observers. + // disconnect observers. removeObservers(this); - // XXX release the reference to the cached file transport service. + // release the reference to the cached file transport service. gFileTransportService = nsnull; return NS_OK; @@ -466,55 +484,89 @@ nsDiskCacheDevice::GetDeviceID() } +/** + * FindEntry - + * + * cases: key not in disk cache, hash number free + * key not in disk cache, hash number used + * key in disk cache + */ nsCacheEntry * nsDiskCacheDevice::FindEntry(nsCString * key) { - // XXX look in entry hashtable first, if not found, then look on - // disk, to see if we have a disk cache entry that maches. - nsCacheEntry * entry = nsnull; + nsCacheEntry * entry = nsnull; nsDiskCacheBindData * bindData = nsnull; - // XXX checking the bound hash table should only be a debug check - // XXX - the cache service shouldn't bother calling the device - // XXX - if the entry is already active (bound or not). -// XXX change to #if DEBUG, because we shouldn't be called for active entries - bindData = mBoundEntries.GetEntry(key->get()); +#if DEBUG /*because we shouldn't be called for active entries */ + bindData = mBindery.GetEntry(key->get()); NS_ASSERTION(!bindData, "### FindEntry() called for a bound entry."); -// XXX #endif - if (!bindData) { - PLDHashNumber hashNumber = nsDiskCacheBindData::Hash(key->get()); - nsDiskCacheRecord* record = mCacheMap->GetRecord(hashNumber); - if (record->HashNumber() == hashNumber) { - nsresult rv = readDiskCacheEntry(key->get(), &bindData); - if (NS_FAILED(rv)) return nsnull; - entry = bindData->getCacheEntry(); - rv = mBoundEntries.AddEntry(bindData); - if (NS_FAILED(rv)) { - delete entry; - return nsnull; - } - } - } else { - // XXX need to make sure this is an exact match, not just - // a hash code match. - entry = bindData->getCacheEntry(); - if (nsCRT::strcmp(entry->Key()->get(), key->get()) != 0) - return nsnull; + bindData = nsnull; +#endif + + PLDHashNumber hashNumber = nsDiskCache::Hash(key->get()); + nsDiskCacheRecord record; + nsresult rv; + + // lookup hash number in cache map + rv = mCacheMap->FindRecord(hashNumber, &record); + if (NS_FAILED(rv)) return nsnull; // XXX log error? + + nsDiskCacheEntry * diskEntry; + rv = mCacheMap->ReadDiskCacheEntry(&record, &diskEntry); + if (NS_FAILED(rv)) goto error_exit; + + entry = diskEntry->CreateCacheEntry(this); + delete diskEntry; // XXX best place? + if (!entry) { + // XXX evict entry in case it's bad + rv = NS_ERROR_UNEXPECTED; + goto error_exit; } + + // XXX pass record to bindery + bindData = mBindery.CreateBindDataForCacheEntry(entry); + if (!bindData) { + delete entry; + } + return entry; + +error_exit: + delete entry; + return nsnull; } nsresult nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry) { - // XXX if entry->mData is null, it's an error. - nsDiskCacheBindData* bindData = ensureDiskCacheBindData(entry); - if (mBoundEntries.GetEntry(bindData->getHashNumber()) == bindData) { - // XXX eventually, as a performance enhancement, keep entries around for a while before deleting them. - // XXX right now, to prove correctness, destroy the entries eagerly. + nsDiskCacheBindData * bindData = GetBindDataFromCacheEntry(entry); + NS_ASSERTION(bindData, "DeactivateEntry: bindData == nsnull"); + if (!bindData) return NS_ERROR_UNEXPECTED; + + if (entry->IsDoomed()) { + // XXX delete data, entry, record from disk for entry + mCacheMap->DecrementTotalSize(entry->DataSize()); // XXX what about meta-data? + } else { + // XXX save stuff to disk for entry + nsDiskCacheEntry * diskEntry = CreateDiskCacheEntry(bindData); + if (!diskEntry) return NS_ERROR_UNEXPECTED; // XXX remove map record? + + nsresult rv = mCacheMap->WriteDiskCacheEntry(diskEntry, bindData); + // XXX if (NS_FAILED(rv)) then what? + } + + // XXX extract bindData from collision detection stuff + + delete entry; // which will release bindData + return NS_OK; + +#if 0 + if (mBoundEntries.GetEntry(bindData->mRecord.HashNumber()) == bindData) { mBoundEntries.RemoveEntry(bindData); } + // XXX else { what if the the entry we're deactivating is further down the list, + // XXX or the head } if (!entry->IsDoomed()) { // commit any changes about this entry to disk. @@ -531,7 +583,7 @@ nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry) delete entry; } else { // keep track of the cache total size. - mCacheMap->DataSize() -= entry->DataSize(); + mCacheMap->DecrementTotalSize(entry->DataSize()); // XXX what about meta-data? // obliterate all knowledge of this entry on disk. deleteDiskCacheEntry(bindData); @@ -548,19 +600,39 @@ nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry) } return NS_OK; +#endif } +/** + * BindEntry() + * no hash number collision -> no problem + * collision + * record not active -> evict, no problem + * record is active + * record is already doomed -> record shouldn't have been in map, no problem + * record is not doomed -> doom, and replace record in map + * + * walk matching hashnumber list to find lowest generation number + * take generation number from other (data/meta) location, + * or walk active list + */ nsresult nsDiskCacheDevice::BindEntry(nsCacheEntry * newEntry) { - nsresult rv; + nsresult rv = NS_OK; + // XXX check cache map or bindery first? // Make sure this entry has its associated nsDiskCacheBindData attached. - nsDiskCacheBindData* newBindData = ensureDiskCacheBindData(newEntry); + // XXX pass new nsDiskCacheRecord to bindery (HashNumber, EvictionRank) + nsDiskCacheBindData * newBindData = mBindery.CreateBindDataForCacheEntry(newEntry); NS_ASSERTION(newBindData, "nsDiskCacheDevice::BindEntry"); if (!newBindData) return NS_ERROR_OUT_OF_MEMORY; + + return rv; + +#if 0 // XXX check for cache collision. if an entry exists on disk that has the same // hash code as this newly bound entry, AND there is already a bound entry for // that key, we need to ask the cache service to doom that entry, since two @@ -604,6 +676,7 @@ nsDiskCacheDevice::BindEntry(nsCacheEntry * newEntry) // only when the entry is deactivated. this could be // the reason disk cache performance suffers. return updateDiskCacheEntry(newBindData); +#endif } @@ -611,9 +684,13 @@ void nsDiskCacheDevice::DoomEntry(nsCacheEntry * entry) { // so it can't be seen by FindEntry() ever again. - // XXX if entry->mData is null, it's an error - nsDiskCacheBindData* bindData = ensureDiskCacheBindData(entry); - mBoundEntries.RemoveEntry(bindData); + nsDiskCacheBindData * bindData = GetBindDataFromCacheEntry(entry); + NS_ASSERTION(bindData, "DoomEntry: bindData == nsnull"); + if (!bindData) return; + +// mBindery.RemoveEntry(bindData); + // XXX but if a subsequent BindEntry() is made for the same HashNumber, + // XXX we need to know what generation number to give it. // XXX clear this entry out of the cache map. // this is done in deleteDiskCacheEntry(). @@ -628,8 +705,9 @@ nsDiskCacheDevice::GetTransportForEntry(nsCacheEntry * entry, NS_ENSURE_ARG_POINTER(entry); NS_ENSURE_ARG_POINTER(result); - nsDiskCacheBindData* bindData = ensureDiskCacheBindData(entry); - if (!bindData) return NS_ERROR_OUT_OF_MEMORY; + nsDiskCacheBindData * bindData = GetBindDataFromCacheEntry(entry); + NS_ASSERTION(bindData, "GetTransportForEntry: bindData == nsnull"); + if (!bindData) return NS_ERROR_UNEXPECTED; #ifdef MOZ_NEW_CACHE_REUSE_TRANSPORTS nsCOMPtr& transport = bindData->getTransport(mode); @@ -637,20 +715,31 @@ nsDiskCacheDevice::GetTransportForEntry(nsCacheEntry * entry, NS_ADDREF(*result = transport); return NS_OK; } -#else - nsCOMPtr transport; #endif - - // XXX generate the name of the cache entry from the hash code of its key, + // XXX check/set bindData->mRecord for separate or block file, sync w/mCacheMap + + // generate the name of the cache entry from the hash code of its key, // modulo the number of files we're willing to keep cached. - nsCOMPtr dataFile; - nsresult rv = getFileForDiskCacheEntry(bindData, PR_FALSE, - getter_AddRefs(dataFile)); - if (NS_SUCCEEDED(rv)) { - rv = getTransportForFile(dataFile, mode, getter_AddRefs(transport)); - if (NS_SUCCEEDED(rv)) - NS_ADDREF(*result = transport); + nsCOMPtr file; + nsresult rv = mCacheMap->GetFileForDiskCacheRecord(&bindData->mRecord, + nsDiskCache::kData, + getter_AddRefs(file)); + if (NS_FAILED(rv)) return rv; + + PRInt32 ioFlags = 0; + switch (mode) { + case nsICache::ACCESS_READ: + ioFlags = PR_RDONLY; + break; + case nsICache::ACCESS_WRITE: + ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE; + break; + case nsICache::ACCESS_READ_WRITE: + ioFlags = PR_RDWR | PR_CREATE_FILE; + break; } + + rv = gFileTransportService->CreateTransport(file, ioFlags, PR_IRUSR | PR_IWUSR, result); return rv; } @@ -659,27 +748,74 @@ nsresult nsDiskCacheDevice::GetFileForEntry(nsCacheEntry * entry, nsIFile ** result) { - nsDiskCacheBindData* bindData = ensureDiskCacheBindData(entry); - if (!bindData) return NS_ERROR_OUT_OF_MEMORY; - return getFileForDiskCacheEntry(bindData, PR_FALSE, result); + nsDiskCacheBindData * bindData = GetBindDataFromCacheEntry(entry); + NS_ASSERTION(bindData, "GetFileForEntry: bindData == nsnull"); + if (!bindData) return NS_ERROR_UNEXPECTED; + + // XXX check/set bindData->mRecord for separate file, sync w/mCacheMap + + nsCOMPtr file; + nsresult rv = mCacheMap->GetFileForDiskCacheRecord(&bindData->mRecord, + nsDiskCache::kData, + getter_AddRefs(file)); + if (NS_FAILED(rv)) return rv; + + NS_IF_ADDREF(*result = file); + return NS_OK; } + /** * This routine will get called every time an open descriptor is written to. */ nsresult nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize) { - PRUint32 newCacheSize = (mCacheMap->DataSize() += deltaSize); - - if (newCacheSize > mCacheCapacity) { - // go toss out some disk cache entries. - evictDiskCacheEntries(); - } + mCacheMap->IncrementTotalSize(deltaSize); + EvictDiskCacheEntries(); return NS_OK; } + +/****************************************************************************** + * EntryInfoVisitor + *****************************************************************************/ +class EntryInfoVisitor : public nsDiskCacheRecordVisitor +{ +public: + EntryInfoVisitor(nsDiskCacheDevice * device, nsICacheVisitor * visitor) + : mDevice(device), mVisitor(visitor) + {} + + virtual PRInt32 VisitRecord(nsDiskCacheRecord * mapRecord) + { + // XXX do we have this record in memory? + // read in the entry (metadata) + nsDiskCacheEntry * diskEntry = (nsDiskCacheEntry *)new char[1]; + + // create nsICacheEntryInfo + nsDiskCacheEntryInfo * entryInfo = new nsDiskCacheEntryInfo(DISK_CACHE_DEVICE_ID, diskEntry); + if (!entryInfo) { + mResult = NS_ERROR_OUT_OF_MEMORY; + return kStopVisitingRecords; + } + nsCOMPtr ref(entryInfo); + // XXX point entryInfo at metadata + + PRBool keepGoing; + nsresult rv = mVisitor->VisitEntry(DISK_CACHE_DEVICE_ID, entryInfo, &keepGoing); + // XXX delete diskEntry; + return keepGoing ? kVisitNextRecord : kStopVisitingRecords; + } + +private: + nsDiskCacheDevice * mDevice; + nsICacheVisitor * mVisitor; + nsresult mResult; +}; + + nsresult nsDiskCacheDevice::Visit(nsICacheVisitor * visitor) { @@ -690,22 +826,49 @@ nsDiskCacheDevice::Visit(nsICacheVisitor * visitor) nsresult rv = visitor->VisitDevice(DISK_CACHE_DEVICE_ID, deviceInfo, &keepGoing); if (NS_FAILED(rv)) return rv; - if (keepGoing) - return visitEntries(visitor); + if (keepGoing) { + EntryInfoVisitor infoVisitor(this, visitor); + return mCacheMap->VisitRecords(&infoVisitor); +// return visitEntries(visitor); + } return NS_OK; } +/****************************************************************************** + * EvictForClientID + *****************************************************************************/ +class EvictForClientID : public nsDiskCacheRecordVisitor +{ +public: + EvictForClientID(nsDiskCacheDevice * device, const char * clientID) + : mDevice(device), mClientID(clientID) + {} + + virtual PRInt32 VisitRecord(nsDiskCacheRecord * mapRecord) + { + return kVisitNextRecord; + } + +private: + nsDiskCacheDevice * mDevice; + const char * mClientID; +}; + + nsresult nsDiskCacheDevice::EvictEntries(const char * clientID) { nsresult rv; - PRUint32 prefixLength = (clientID ? nsCRT::strlen(clientID) : 0); - PRUint32 newDataSize = mCacheMap->DataSize(); - PRUint32 newEntryCount = mCacheMap->EntryCount(); + PRUint32 prefixLength = (clientID ? nsCRT::strlen(clientID) : 0); + PRInt32 newDataSize = mCacheMap->TotalSize(); + PRInt32 newEntryCount = mCacheMap->EntryCount(); + EvictForClientID evictor(this, clientID); + rv = mCacheMap->VisitRecords(&evictor); +#if 0 // XXX make sure meta data is up to date. rv = updateDiskCacheEntries(); if (NS_FAILED(rv)) return rv; @@ -786,14 +949,97 @@ nsDiskCacheDevice::EvictEntries(const char * clientID) mCacheMap->DataSize() = 0; mCacheMap->EntryCount() = 0; } +#endif return NS_OK; } +/** + * private methods + */ +#ifdef XP_MAC +#pragma mark - +#pragma mark PRIVATE METHODS +#endif + +nsresult +nsDiskCacheDevice::InitializeCacheDirectory() +{ + nsresult rv; + + // recursively delete the disk cache directory. + rv = mCacheDirectory->Delete(PR_TRUE); + if (NS_FAILED(rv)) { + // try moving it aside + + // create "Cache.Trash" directory if necessary + nsCOMPtr cacheTrashDir; + rv = GetCacheTrashDirectory(getter_AddRefs(cacheTrashDir)); + if (NS_FAILED(rv)) return rv; + + PRBool exists = PR_FALSE; + rv = cacheTrashDir->Exists(&exists); + if (NS_FAILED(rv)) return rv; + + if (!exists) { + // create the "Cache.Trash" directory + rv = cacheTrashDir->Create(nsIFile::DIRECTORY_TYPE,0777); + if (NS_FAILED(rv)) return rv; + } + + // create a directory with unique name to contain existing cache directory + rv = cacheTrashDir->Append("Cache"); + if (NS_FAILED(rv)) return rv; + rv = cacheTrashDir->CreateUnique(nsnull,nsIFile::DIRECTORY_TYPE, 0777); + if (NS_FAILED(rv)) return rv; + + // move existing cache directory into profileDir/Cache.Trash/CacheUnique + nsCOMPtr existingCacheDir; + rv = mCacheDirectory->Clone(getter_AddRefs(existingCacheDir)); + if (NS_FAILED(rv)) return rv; + rv = existingCacheDir->MoveTo(cacheTrashDir, nsnull); + if (NS_FAILED(rv)) return rv; + } + + rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777); + if (NS_FAILED(rv)) return rv; + + // reopen the cache map + rv = mCacheMap->Open(mCacheDirectory); + return rv; +} + + +nsresult +nsDiskCacheDevice::GetCacheTrashDirectory(nsIFile ** result) +{ + nsCOMPtr cacheTrashDir; + nsresult rv = mCacheDirectory->Clone(getter_AddRefs(cacheTrashDir)); + if (NS_FAILED(rv)) return rv; + rv = cacheTrashDir->SetLeafName("Cache.Trash"); + if (NS_FAILED(rv)) return rv; + + *result = cacheTrashDir.get(); + NS_ADDREF(*result); + return rv; +} + + +nsresult +nsDiskCacheDevice::EvictDiskCacheEntries() +{ + // XXX mCacheMap->GetMeTheNextRecordToEvict(); + return NS_OK; +} + /** * methods for prefs */ +#ifdef XP_MAC +#pragma mark - +#pragma mark PREF METHODS +#endif void nsDiskCacheDevice::setPrefsObserver(nsIObserver* observer) { mPrefsObserver = observer; @@ -822,10 +1068,9 @@ void nsDiskCacheDevice::setCacheCapacity(PRUint32 capacity) { mCacheCapacity = capacity; if (mInitialized) { - // XXX start evicting entries if the new size is smaller! + // start evicting entries if the new size is smaller! // XXX need to enter cache service lock here! - if (mCacheMap->DataSize() > capacity) - evictDiskCacheEntries(); + EvictDiskCacheEntries(); } } @@ -836,7 +1081,7 @@ PRUint32 nsDiskCacheDevice::getCacheCapacity() PRUint32 nsDiskCacheDevice::getCacheSize() { - return mCacheMap->DataSize(); + return mCacheMap->TotalSize(); } PRUint32 nsDiskCacheDevice::getEntryCount() @@ -844,104 +1089,36 @@ PRUint32 nsDiskCacheDevice::getEntryCount() return mCacheMap->EntryCount(); } -/** - * private methods - */ -nsresult nsDiskCacheDevice::getFileForHashNumber(PLDHashNumber hashNumber, PRBool meta, PRUint32 generation, nsIFile ** result) + + +/****************************************************************************** + * Old Disk Cache Code + *****************************************************************************/ +#ifdef XP_MAC +#pragma mark - +#pragma mark OLD DISK CACHE CODE +#endif + + #if 0 +// called from BindEntry(), FindEntry(nsCString * key)->readDiskCacheEntry +static nsDiskCacheBindData* +ensureDiskCacheBindData(nsCacheEntry * entry) { - if (mCacheDirectory) { - nsCOMPtr entryFile; - nsresult rv = mCacheDirectory->Clone(getter_AddRefs(entryFile)); - if (NS_FAILED(rv)) - return rv; - // generate the hash code for this entry, and use that as a file name. - char name[32]; - ::sprintf(name, "%08X%c%02X", hashNumber, (meta ? 'm' : 'd'), generation); - entryFile->Append(name); - NS_ADDREF(*result = entryFile); - return NS_OK; + nsCOMPtr data; + nsresult rv = entry->GetData(getter_AddRefs(data)); + if (NS_SUCCEEDED(rv) && !data) { + nsDiskCacheBindData* bindData = new nsDiskCacheBindData(entry); + data = bindData; + if (NS_SUCCEEDED(rv) && data) + entry->SetData(data.get()); } - return NS_ERROR_NOT_AVAILABLE; -} -nsresult nsDiskCacheDevice::getFileForKey(const char* key, PRBool meta, - PRUint32 generation, nsIFile ** result) -{ - PLDHashNumber hash = nsDiskCacheBindData::Hash(key); - return getFileForHashNumber(hash, meta, generation, result); + return (nsDiskCacheBindData*) (nsISupports*) data.get(); } -nsresult nsDiskCacheDevice::getFileForDiskCacheEntry(nsDiskCacheBindData * bindData, PRBool meta, - nsIFile ** result) -{ - return getFileForHashNumber(bindData->getHashNumber(), meta, bindData->getGeneration(), result); -} - -nsresult nsDiskCacheDevice::getTransportForFile(nsIFile* file, nsCacheAccessMode mode, nsITransport ** result) -{ - PRInt32 ioFlags = 0; - switch (mode) { - case nsICache::ACCESS_READ: - ioFlags = PR_RDONLY; - break; - case nsICache::ACCESS_WRITE: - ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE; - break; - case nsICache::ACCESS_READ_WRITE: - ioFlags = PR_RDWR | PR_CREATE_FILE; - break; - } - return gFileTransportService->CreateTransport(file, ioFlags, PR_IRUSR | PR_IWUSR, result); -} - -nsresult nsDiskCacheDevice::openInputStream(nsIFile * file, nsIInputStream ** result) -{ - nsresult rv; - nsCOMPtr localFile = do_QueryInterface(file); - if (localFile) { - nsANSIInputStream* stream = new nsANSIInputStream; - if (!stream) return NS_ERROR_OUT_OF_MEMORY; - nsCOMPtr input(stream); - rv = stream->Open(localFile); - if (NS_FAILED(rv)) return rv; - NS_ADDREF(*result = input); - return NS_OK; - } else { - nsCOMPtr transport; - rv = getTransportForFile(file, nsICache::ACCESS_READ, getter_AddRefs(transport)); - if (NS_FAILED(rv)) return rv; - return transport->OpenInputStream(0, ULONG_MAX, 0, result); - } -} - -nsresult nsDiskCacheDevice::openOutputStream(nsIFile * file, nsIOutputStream ** result) -{ - nsresult rv; - nsCOMPtr localFile = do_QueryInterface(file); - if (localFile) { - nsANSIOutputStream* stream = new nsANSIOutputStream; - if (!stream) return NS_ERROR_OUT_OF_MEMORY; - nsCOMPtr output(stream); - rv = stream->Open(localFile); - if (NS_FAILED(rv)) return rv; - NS_ADDREF(*result = output); - return NS_OK; - } else { - nsCOMPtr transport; - rv = getTransportForFile(file, nsICache::ACCESS_WRITE, getter_AddRefs(transport)); - if (NS_FAILED(rv)) return rv; - return transport->OpenOutputStream(0, ULONG_MAX, 0, result); - } -} - -inline PRBool isMetaDataFile(const char* name) -{ - return (name[8] == 'm'); -} nsresult nsDiskCacheDevice::visitEntries(nsICacheVisitor * visitor) { - nsresult rv; - + nsresult rv = NS_OK; // XXX make sure meta data is up to date. rv = updateDiskCacheEntries(); if (NS_FAILED(rv)) return rv; @@ -950,6 +1127,7 @@ nsresult nsDiskCacheDevice::visitEntries(nsICacheVisitor * visitor) if (!entryInfo) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr ref(entryInfo); + for (PRUint32 i = 0; i < kBucketsPerTable; ++i) { nsDiskCacheRecord* bucket = mCacheMap->GetBucket(i); for (PRUint32 j = 0; j < kRecordsPerBucket; ++j) { @@ -957,7 +1135,8 @@ nsresult nsDiskCacheDevice::visitEntries(nsICacheVisitor * visitor) if (record->HashNumber() == 0) break; nsCOMPtr file; - rv = getFileForHashNumber(record->HashNumber(), PR_TRUE, record->FileGeneration(), getter_AddRefs(file)); + rv = getFileForHashNumber(record->HashNumber(), PR_TRUE, + record->FileGeneration(), getter_AddRefs(file)); if (NS_FAILED(rv)) continue; nsCOMPtr input; @@ -981,10 +1160,10 @@ nsresult nsDiskCacheDevice::visitEntries(nsICacheVisitor * visitor) if (!keepGoing) break; } } - return NS_OK; } + class UpdateEntryVisitor : public nsDiskCacheHashTable::Visitor { nsDiskCacheDevice* mDevice; public: @@ -1067,7 +1246,7 @@ nsresult nsDiskCacheDevice::readDiskCacheEntry(const char * key, nsDiskCacheBind if (nsCRT::strcmp(key, metaDataFile.mKey) != 0) return NS_ERROR_NOT_AVAILABLE; nsCacheEntry* entry = nsnull; - rv = CreateCacheEntry(key, PR_TRUE, nsICache::STORE_ON_DISK, this, &entry); + rv = nsCacheEntry::Create(key, nsICache::STREAM_BASED, nsICache::STORE_ON_DISK, this, &entry); if (NS_FAILED(rv)) return rv; // initialize the entry. @@ -1094,20 +1273,28 @@ error: return NS_ERROR_NOT_AVAILABLE; } + +/** + * DeleteDiskCacheEntry - only called from DeactivateEntry on doomed entries + */ nsresult nsDiskCacheDevice::deleteDiskCacheEntry(nsDiskCacheBindData * bindData) { nsresult rv; - // delete the metadata file. - nsCOMPtr metaFile; - rv = getFileForDiskCacheEntry(bindData, PR_TRUE, - getter_AddRefs(metaFile)); - if (NS_SUCCEEDED(rv)) { - rv = metaFile->Delete(PR_FALSE); - // NS_ASSERTION(NS_SUCCEEDED(rv), "nsDiskCacheDevice::deleteDiskCacheEntry"); + // delete the metadata + if (bindData->record.MetaLocation() == 0) { + nsCOMPtr metaFile; + rv = getFileForDiskCacheEntry(bindData->record.MetaLocation(), kMetaData, + getter_AddRefs(metaFile)); + if (NS_SUCCEEDED(rv)) { + rv = metaFile->Delete(PR_FALSE); + // NS_ASSERTION(NS_SUCCEEDED(rv), "nsDiskCacheDevice::deleteDiskCacheEntry"); + } + } else { + // XXX delete blocks in block file } - // delete the data file + // delete the data nsCOMPtr dataFile; rv = getFileForDiskCacheEntry(bindData, PR_FALSE, getter_AddRefs(dataFile)); @@ -1116,7 +1303,7 @@ nsresult nsDiskCacheDevice::deleteDiskCacheEntry(nsDiskCacheBindData * bindData) // NS_ASSERTION(NS_SUCCEEDED(rv), "nsDiskCacheDevice::deleteDiskCacheEntry"); } - // remove from cache map. + // delete disk cache record nsDiskCacheRecord* record = mCacheMap->GetRecord(bindData->getHashNumber()); if (record->HashNumber() == bindData->getHashNumber() && record->FileGeneration() == bindData->getGeneration()) mCacheMap->DeleteRecord(record); @@ -1124,6 +1311,56 @@ nsresult nsDiskCacheDevice::deleteDiskCacheEntry(nsDiskCacheBindData * bindData) return NS_OK; } + +nsresult nsDiskCacheDevice::getTransportForFile(nsIFile* file, nsCacheAccessMode mode, nsITransport ** result) +{ + PRInt32 ioFlags = 0; + switch (mode) { + case nsICache::ACCESS_READ: + ioFlags = PR_RDONLY; + break; + case nsICache::ACCESS_WRITE: + ioFlags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE; + break; + case nsICache::ACCESS_READ_WRITE: + ioFlags = PR_RDWR | PR_CREATE_FILE; + break; + } + return gFileTransportService->CreateTransport(file, ioFlags, PR_IRUSR | PR_IWUSR, result); +} + + +nsresult nsDiskCacheDevice::getFileForHashNumber(PLDHashNumber hashNumber, PRBool meta, PRUint32 generation, nsIFile ** result) +{ + if (mCacheDirectory) { + nsCOMPtr entryFile; + nsresult rv = mCacheDirectory->Clone(getter_AddRefs(entryFile)); + if (NS_FAILED(rv)) + return rv; + // generate the hash code for this entry, and use that as a file name. + char name[32]; + ::sprintf(name, "%08X%c%02X", hashNumber, (meta ? 'm' : 'd'), generation); + entryFile->Append(name); + NS_ADDREF(*result = entryFile); + return NS_OK; + } + return NS_ERROR_NOT_AVAILABLE; +} +nsresult nsDiskCacheDevice::getFileForKey(const char* key, PRBool meta, + PRUint32 generation, nsIFile ** result) +{ + PLDHashNumber hash = nsDiskCacheBindData::Hash(key); + return getFileForHashNumber(hash, meta, generation, result); +} + + +nsresult nsDiskCacheDevice::getFileForDiskCacheEntry(nsDiskCacheBindData* bindData, + PRBool meta, + nsIFile ** result) +{ + return getFileForHashNumber(bindData->getHashNumber(), meta, bindData->getGeneration(), result); +} + nsresult nsDiskCacheDevice::scavengeDiskCacheEntries(nsDiskCacheBindData * bindData) { nsresult rv; @@ -1180,67 +1417,6 @@ nsresult nsDiskCacheDevice::scavengeDiskCacheEntries(nsDiskCacheBindData * bindD } -nsresult nsDiskCacheDevice::InitializeCacheDirectory() -{ - nsresult rv; - - // close the cache map file. - NS_IF_RELEASE(mCacheStream); - - // recursively delete the disk cache directory. - rv = mCacheDirectory->Delete(PR_TRUE); - if (NS_FAILED(rv)) { - // try moving it aside - - // get parent directory of cache directory - nsCOMPtr cacheParentDir; - rv = mCacheDirectory->GetParent(getter_AddRefs(cacheParentDir)); - if (NS_FAILED(rv)) return rv; - - // create "Cache.Trash" directory if necessary - nsCOMPtr oldCacheDir; - rv = cacheParentDir->Clone(getter_AddRefs(oldCacheDir)); - if (NS_FAILED(rv)) return rv; - rv = oldCacheDir->Append("Cache.Trash"); - if (NS_FAILED(rv)) return rv; - - PRBool exists = PR_FALSE; - rv = oldCacheDir->Exists(&exists); - if (NS_FAILED(rv)) return rv; - - if (!exists) { - // create the "Cache.Trash" directory - rv = oldCacheDir->Create(nsIFile::DIRECTORY_TYPE,0777); - if (NS_FAILED(rv)) return rv; - } - - // create a directory with unique name to contain existing cache directory - rv = oldCacheDir->Append("Cache"); - if (NS_FAILED(rv)) return rv; - rv = oldCacheDir->CreateUnique(nsnull,nsIFile::DIRECTORY_TYPE, 0777); - if (NS_FAILED(rv)) return rv; - - // move existing cache directory into profileDir/Cache.Trash/CacheUnique - nsCOMPtr existingCacheDir; - rv = mCacheDirectory->Clone(getter_AddRefs(existingCacheDir)); - if (NS_FAILED(rv)) return rv; - rv = existingCacheDir->MoveTo(oldCacheDir, nsnull); - if (NS_FAILED(rv)) return rv; - } - - rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777); - if (NS_FAILED(rv)) return rv; - - // reset the cache map - mCacheMap->Reset(); - - rv = openCacheMap(); - if (NS_FAILED(rv)) return rv; - - rv = mCacheMap->Write(mCacheStream); - return rv; -} - PR_STATIC_CALLBACK(int) compareRecords(const void* e1, const void* e2, void* /*unused*/) @@ -1254,7 +1430,7 @@ nsresult nsDiskCacheDevice::evictDiskCacheEntries() { nsresult rv; - if (mCacheMap->DataSize() < mCacheCapacity) return NS_OK; + if (mCacheMap->TotalSize() < mCacheCapacity) return NS_OK; // XXX make sure meta data is up to date. rv = updateDiskCacheEntries(); @@ -1324,62 +1500,12 @@ nsresult nsDiskCacheDevice::evictDiskCacheEntries() } -/** - * openCacheMap : called by InitializeCacheDirectory, readCacheMap, writeCacheMap, updateCacheMap - */ -nsresult nsDiskCacheDevice::openCacheMap() -{ - nsresult rv; - nsCOMPtr file; - rv = mCacheDirectory->Clone(getter_AddRefs(file)); - if (NS_FAILED(rv)) return rv; - nsCOMPtr localFile(do_QueryInterface(file, &rv)); - if (NS_FAILED(rv)) return rv; - rv = localFile->Append("_CACHE_MAP_"); - if (NS_FAILED(rv)) return rv; - - mCacheStream = new nsANSIFileStream; - if (!mCacheStream) return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mCacheStream); - rv = mCacheStream->Open(localFile); - if (NS_FAILED(rv)) { - NS_RELEASE(mCacheStream); - return rv; - } - - return NS_OK; -} - -nsresult nsDiskCacheDevice::readCacheMap() // only called by Init() -{ - nsresult rv; - if (!mCacheStream) { - rv = openCacheMap(); - if (NS_FAILED(rv)) return rv; - } - rv = mCacheMap->Read(mCacheStream); - return rv; -} - -nsresult nsDiskCacheDevice::writeCacheMap() // only called by Shutdown() -{ - nsresult rv; - if (!mCacheStream) { - rv = openCacheMap(); - if (NS_FAILED(rv)) return rv; - } - rv = mCacheMap->Write(mCacheStream); - return rv; -} - - /** * updateCacheMap : called by updateDiskCacheEntry */ nsresult nsDiskCacheDevice::updateCacheMap(nsDiskCacheBindData * bindData) { - nsresult rv; - + nsresult rv = NS_OK; // get a record from the cache map, and use the fetch time for eviction ranking. PRBool commitBucket = PR_TRUE; nsDiskCacheRecord* record = mCacheMap->GetRecord(bindData->getHashNumber()); @@ -1416,7 +1542,6 @@ nsresult nsDiskCacheDevice::updateCacheMap(nsDiskCacheBindData * bindData) } rv = mCacheMap->WriteBucket(mCacheStream, mCacheMap->GetBucketIndex(record)); } - return rv; } @@ -1452,3 +1577,4 @@ nsresult nsDiskCacheDevice::evictDiskCacheRecord(nsDiskCacheRecord * record) return rv; } +#endif diff --git a/mozilla/netwerk/cache/src/nsDiskCacheDevice.h b/mozilla/netwerk/cache/src/nsDiskCacheDevice.h index fc539f1851a..670ba40bade 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheDevice.h +++ b/mozilla/netwerk/cache/src/nsDiskCacheDevice.h @@ -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 mPrefsObserver; // XXX ? nsCOMPtr 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_ diff --git a/mozilla/netwerk/cache/src/nsDiskCacheEntry.cpp b/mozilla/netwerk/cache/src/nsDiskCacheEntry.cpp index 7a3adc0203b..a7db1a37186 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheEntry.cpp +++ b/mozilla/netwerk/cache/src/nsDiskCacheEntry.cpp @@ -22,79 +22,121 @@ * Patrick C. Beard */ +#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; } diff --git a/mozilla/netwerk/cache/src/nsDiskCacheEntry.h b/mozilla/netwerk/cache/src/nsDiskCacheEntry.h index a93350c34f9..f96da442225 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheEntry.h +++ b/mozilla/netwerk/cache/src/nsDiskCacheEntry.h @@ -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; }; diff --git a/mozilla/netwerk/cache/src/nsDiskCacheMap.cpp b/mozilla/netwerk/cache/src/nsDiskCacheMap.cpp index 71642fac923..8ab8d6ed026 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheMap.cpp +++ b/mozilla/netwerk/cache/src/nsDiskCacheMap.cpp @@ -23,94 +23,166 @@ */ #include "nsDiskCacheMap.h" +#include "nsDiskCacheEntry.h" -#include "nsIFileStreams.h" #include "nsCRT.h" #include -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 file; + rv = cacheDirectory->Clone(getter_AddRefs(file)); + nsCOMPtr 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 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 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 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 file; + nsresult rv = GetFileForDiskCacheRecord(record, meta, getter_AddRefs(file)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr 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 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 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 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; -} diff --git a/mozilla/netwerk/cache/src/nsDiskCacheMap.h b/mozilla/netwerk/cache/src/nsDiskCacheMap.h index 67b210fb4d4..82fe28f01e9 100644 --- a/mozilla/netwerk/cache/src/nsDiskCacheMap.h +++ b/mozilla/netwerk/cache/src/nsDiskCacheMap.h @@ -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 mCacheDirectory; + PRFileDesc * mMapFD; + nsDiskCacheBlockFile mBlockFile[3]; nsDiskCacheHeader mHeader; nsDiskCacheBucket mBuckets[kBucketsPerTable]; };