зеркало из https://github.com/mozilla/pjs.git
337 строки
8.9 KiB
C++
337 строки
8.9 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* nsMemCache is the implementation of an in-memory network-data
|
|
* cache, used to cache the responses to network retrieval commands.
|
|
* Each cache entry may contain both content, e.g. GIF image data, and
|
|
* associated metadata, e.g. HTTP headers. Each entry is indexed by
|
|
* two different keys: a record id number and an opaque key, which is
|
|
* created by the cache manager by combining the URI with a "secondary
|
|
* key", e.g. HTTP post data.
|
|
*/
|
|
|
|
#include "nsMemCache.h"
|
|
#include "nsMemCacheRecord.h"
|
|
#include "nsIGenericFactory.h"
|
|
#include "nsString.h"
|
|
#include "nsHashtable.h"
|
|
#include "nsHashtableEnumerator.h"
|
|
#include "nsEnumeratorUtils.h"
|
|
|
|
PRInt32 nsMemCache::gRecordSerialNumber = 0;
|
|
|
|
nsMemCache::nsMemCache()
|
|
: mNumEntries(0), mOccupancy(0), mEnabled(PR_TRUE),
|
|
mHashTable(0)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
nsMemCache::~nsMemCache()
|
|
{
|
|
nsresult rv;
|
|
|
|
rv = RemoveAll();
|
|
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv) && (mNumEntries == 0),
|
|
"Failure to shut down memory cache cleanly. "
|
|
"Somewhere, someone is holding references to at least one cache record");
|
|
delete mHashTable;
|
|
}
|
|
|
|
nsresult
|
|
nsMemCache::Init()
|
|
{
|
|
mHashTable = new nsHashtable(256);
|
|
if (!mHashTable)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(nsMemCache, NS_GET_IID(nsINetDataCache))
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetDescription(PRUnichar * *aDescription)
|
|
{
|
|
nsAutoString description;
|
|
description.AssignWithConversion("Memory Cache");
|
|
|
|
*aDescription = description.ToNewUnicode();
|
|
if (!*aDescription)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::Contains(const char *aKey, PRUint32 aKeyLength, PRBool *aFound)
|
|
{
|
|
nsOpaqueKey *opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
|
|
if (!opaqueKey)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
*aFound = mHashTable->Exists(opaqueKey);
|
|
delete opaqueKey;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetCachedNetData(const char *aKey, PRUint32 aKeyLength,
|
|
nsINetDataCacheRecord* *aRecord)
|
|
{
|
|
nsresult rv;
|
|
nsMemCacheRecord* record = 0;
|
|
nsOpaqueKey *opaqueKey2 = 0;
|
|
nsOpaqueKey *opaqueKey3 = 0;
|
|
nsOpaqueKey *opaqueKey;
|
|
|
|
opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
|
|
if (!opaqueKey)
|
|
goto out_of_memory;
|
|
record = (nsMemCacheRecord*)mHashTable->Get(opaqueKey);
|
|
delete opaqueKey;
|
|
|
|
// No existing cache database entry was found. Create a new one.
|
|
// This requires two mappings in the hash table:
|
|
// Record ID ==> record
|
|
// Opaque key ==> record
|
|
if (!record) {
|
|
record = new nsMemCacheRecord;
|
|
if (!record)
|
|
goto out_of_memory;
|
|
rv = record->Init(aKey, aKeyLength, ++gRecordSerialNumber, this);
|
|
if (NS_FAILED(rv)) goto out_of_memory;
|
|
|
|
// Index the record by opaque key
|
|
opaqueKey2 = new nsOpaqueKey(record->mKey, record->mKeyLength);
|
|
if (!opaqueKey2) goto out_of_memory;
|
|
mHashTable->Put(opaqueKey2, record);
|
|
|
|
// Index the record by it's record ID
|
|
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &record->mRecordID);
|
|
opaqueKey3 = new nsOpaqueKey(recordIDbytes,
|
|
sizeof record->mRecordID);
|
|
if (!opaqueKey3) {
|
|
// Clean up the first record from the hash table
|
|
mHashTable->Remove(opaqueKey);
|
|
goto out_of_memory;
|
|
}
|
|
mHashTable->Put(opaqueKey3, record);
|
|
|
|
// The hash table holds on to the record
|
|
record->AddRef();
|
|
|
|
delete opaqueKey2;
|
|
delete opaqueKey3;
|
|
mNumEntries++;
|
|
}
|
|
|
|
record->AddRef();
|
|
*aRecord = record;
|
|
return NS_OK;
|
|
|
|
out_of_memory:
|
|
delete opaqueKey2;
|
|
delete opaqueKey3;
|
|
delete record;
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetCachedNetDataByID(PRInt32 RecordID,
|
|
nsINetDataCacheRecord* *aRecord)
|
|
{
|
|
nsOpaqueKey opaqueKey(NS_REINTERPRET_CAST(const char *, &RecordID),
|
|
sizeof RecordID);
|
|
*aRecord = (nsINetDataCacheRecord*)mHashTable->Get(&opaqueKey);
|
|
if (*aRecord) {
|
|
NS_ADDREF(*aRecord);
|
|
return NS_OK;
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsMemCache::Delete(nsMemCacheRecord* aRecord)
|
|
{
|
|
nsMemCacheRecord *removedRecord;
|
|
|
|
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &aRecord->mRecordID);
|
|
nsOpaqueKey opaqueRecordIDKey(recordIDbytes,
|
|
sizeof aRecord->mRecordID);
|
|
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueRecordIDKey);
|
|
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
|
|
|
|
nsOpaqueKey opaqueKey(aRecord->mKey, aRecord->mKeyLength);
|
|
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueKey);
|
|
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
|
|
|
|
aRecord->Release();
|
|
|
|
mNumEntries--;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetEnabled(PRBool *aEnabled)
|
|
{
|
|
NS_ENSURE_ARG(aEnabled);
|
|
*aEnabled = mEnabled;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::SetEnabled(PRBool aEnabled)
|
|
{
|
|
mEnabled = aEnabled;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetFlags(PRUint32 *aFlags)
|
|
{
|
|
NS_ENSURE_ARG(aFlags);
|
|
*aFlags = MEMORY_CACHE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetNumEntries(PRUint32 *aNumEntries)
|
|
{
|
|
NS_ENSURE_ARG(aNumEntries);
|
|
*aNumEntries = mNumEntries;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetMaxEntries(PRUint32 *aMaxEntries)
|
|
{
|
|
NS_ENSURE_ARG(aMaxEntries);
|
|
*aMaxEntries = MEM_CACHE_MAX_ENTRIES;
|
|
return NS_OK;
|
|
}
|
|
|
|
static NS_METHOD
|
|
HashEntryConverter(nsHashKey *aKey, void *aValue,
|
|
void *unused, nsISupports **retval)
|
|
{
|
|
nsMemCacheRecord *record;
|
|
nsOpaqueKey *opaqueKey;
|
|
|
|
record = (nsMemCacheRecord*)aValue;
|
|
opaqueKey = (nsOpaqueKey*)aKey;
|
|
|
|
// Hash table keys that index cache entries by their record ID
|
|
// shouldn't be enumerated.
|
|
if ((opaqueKey->GetKeyLength() == sizeof(PRInt32))) {
|
|
|
|
#ifdef DEBUG
|
|
PRInt32 recordID;
|
|
record->GetRecordID(&recordID);
|
|
NS_ASSERTION(*((PRInt32*)opaqueKey->GetKey()) == recordID,
|
|
"Key has incorrect key length");
|
|
#endif
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IF_ADDREF(record);
|
|
*retval = NS_STATIC_CAST(nsISupports*, record);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::NewCacheEntryIterator(nsISimpleEnumerator* *aIterator)
|
|
{
|
|
nsCOMPtr<nsIEnumerator> iterator;
|
|
|
|
NS_ENSURE_ARG(aIterator);
|
|
NS_NewHashtableEnumerator(mHashTable, HashEntryConverter,
|
|
mHashTable, getter_AddRefs(iterator));
|
|
return NS_NewAdapterEnumerator(aIterator, iterator);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetNextCache(nsINetDataCache* *aNextCache)
|
|
{
|
|
NS_ENSURE_ARG(aNextCache);
|
|
*aNextCache = mNextCache;
|
|
NS_ADDREF(*aNextCache);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::SetNextCache(nsINetDataCache* aNextCache)
|
|
{
|
|
mNextCache = aNextCache;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::GetStorageInUse(PRUint32 *aStorageInUse)
|
|
{
|
|
NS_ENSURE_ARG(aStorageInUse);
|
|
|
|
// Convert from bytes to KB
|
|
*aStorageInUse = (mOccupancy >> 10);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMemCache::RemoveAll(void)
|
|
{
|
|
PRBool failed;
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> iterator;
|
|
nsCOMPtr<nsISupports> recordSupports;
|
|
nsCOMPtr<nsINetDataCacheRecord> record;
|
|
nsresult rv;
|
|
|
|
failed = PR_FALSE;
|
|
rv = NewCacheEntryIterator(getter_AddRefs(iterator));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
PRBool notDone;
|
|
while (1) {
|
|
rv = iterator->HasMoreElements(¬Done);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!notDone)
|
|
break;
|
|
|
|
iterator->GetNext(getter_AddRefs(recordSupports));
|
|
record = do_QueryInterface(recordSupports);
|
|
recordSupports = 0;
|
|
|
|
PRUint32 bytesUsed;
|
|
record->GetStoredContentLength(&bytesUsed);
|
|
rv = record->Delete();
|
|
if (NS_FAILED(rv)) {
|
|
failed = PR_TRUE;
|
|
continue;
|
|
}
|
|
mOccupancy -= bytesUsed;
|
|
}
|
|
|
|
if (failed)
|
|
return NS_ERROR_FAILURE;
|
|
return NS_OK;
|
|
}
|