Files
tubestation/netwerk/cache/filecache/nsDiskCacheRecord.cpp
dougt@netscape.com a70fce6f87 Revising nsIChannel to allow for overlapped i/o. This consists of three parts:
1. Factoring nsIChannel into a protocol specific part, the nsIChannel, and a socket specific, the nsITransport.
2. Derive the nsIChannel from a nsIRequest.
2. Changes the notification system from necko and the URILoader to pass the nsIRequest interface instead of nsIChannel interface.

This goal stems from wanting to be able to have active AsyncRead and AsyncWrite operations on nsSocketTransport.
This is desired because it would greatly simplify the task of maintaining persistent/reusable socket connections
for FTP, HTTP, and Imap (and potentially other protocols).  The problem with the existing nsIChannel interface is
that it does not allow one to selectively suspend just one of the read or write operations while keeping the other active.

The full details of the change on written up in the netlib newsgroup.

r=darin@netscape.com
sr=rpotts@netscape.com
2001-02-10 00:16:26 +00:00

566 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsDiskCacheRecord.h"
#include "nsINetDataDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsDiskCacheRecordChannel.h"
#include "nsFileStream.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIProtocolHandler.h"
#include "nsIIOService.h"
#include "nsMemory.h"
#include "plstr.h"
#include "prprf.h"
#include "prmem.h"
#include "prlog.h"
#include "prtypes.h"
#include "netCore.h"
#include "nsDBAccessor.h"
#include "nsILocalFile.h"
#if !defined(IS_LITTLE_ENDIAN) && !defined(IS_BIG_ENDIAN)
ERROR! Must have a byte order
#endif
#ifdef IS_LITTLE_ENDIAN
#define COPY_INT32(_a,_b) memcpy(_a, _b, sizeof(int32))
#else
#define COPY_INT32(_a,_b) /* swap */ \
do { \
((char *)(_a))[0] = ((char *)(_b))[3]; \
((char *)(_a))[1] = ((char *)(_b))[2]; \
((char *)(_a))[2] = ((char *)(_b))[1]; \
((char *)(_a))[3] = ((char *)(_b))[0]; \
} while(0)
#endif
nsDiskCacheRecord::nsDiskCacheRecord(nsIDBAccessor* db, nsNetDiskCache* aCache) :
mKey(0) ,
mKeyLength(0) ,
mRecordID(0) ,
mMetaData(0) ,
mMetaDataLength(0) ,
mDB(db) ,
mInfo(0) ,
mInfoSize(0) ,
mNumTransports(0) ,
mDiskCache(aCache)
{
NS_INIT_REFCNT();
NS_ASSERTION(mDiskCache, "Must have an nsNetDiskCache");
NS_ADDREF(mDiskCache);
#ifdef DEBUG_dp
printf("CACHE: nsDiskCacheRecord(): %p created\n", this);
#endif /* DEBUG_dp */
}
// mem alloced. so caller should do free() on key.
NS_IMETHODIMP
nsDiskCacheRecord::Init(const char* key, PRUint32 length, PRInt32 ID)
{
// copy key
mKeyLength = length ;
mKey = NS_STATIC_CAST(char*, nsMemory::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, key, length) ;
// get RecordID
mRecordID = ID ;
// setup the file name
nsCOMPtr<nsIFile> dbFolder ;
mDiskCache->GetDiskCacheFolder(getter_AddRefs(dbFolder)) ;
nsresult rv = dbFolder->Clone( getter_AddRefs( mFile ) );
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
// dir is a hash result of mRecordID%32, hope it's enough
char filename[9], dirName[3] ;
PR_snprintf(dirName, 3, "%02x", (((PRUint32)mRecordID) % 32)) ;
mFile->Append(dirName) ;
PR_snprintf(filename, 9, "%08x", mRecordID) ;
mFile->Append(filename) ;
return NS_OK ;
}
nsDiskCacheRecord::~nsDiskCacheRecord()
{
#ifdef DEBUG_dp
printf("CACHE: ~nsDiskCacheRecord(%d, %s): %p destroyed\n", mRecordID, mKey, this);
#endif /* DEBUG_dp */
if(mKey)
nsMemory::Free(mKey) ;
if(mMetaData)
nsMemory::Free(mMetaData) ;
if(mInfo)
nsMemory::Free(mInfo) ;
NS_IF_RELEASE(mDiskCache);
}
//
// Implement nsISupports methods
//
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDiskCacheRecord, nsINetDataCacheRecord)
///////////////////////////////////////////////////////////////////////
// nsINetDataCacheRecord methods
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetKey(PRUint32 *length, char** _retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*length = mKeyLength ;
*_retval = NS_STATIC_CAST(char*, nsMemory::Alloc(mKeyLength*sizeof(char) + 1)) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mKey, mKeyLength) ;
(*_retval)[mKeyLength] = '\0'; // null terminate because this gets used in a string key for a hashtable
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetRecordID(PRInt32* aRecordID)
{
*aRecordID = mRecordID ;
return NS_OK ;
}
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetMetaData(PRUint32 *length, char **_retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
// always null the return value first.
*_retval = nsnull ;
*length = mMetaDataLength ;
if(mMetaDataLength) {
*_retval = NS_STATIC_CAST(char*, nsMemory::Alloc(mMetaDataLength*sizeof(char))) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mMetaData, mMetaDataLength) ;
}
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::SetMetaData(PRUint32 length, const char* data)
{
// set the mMetaData
mMetaDataLength = length ;
if(mMetaData)
nsMemory::Free(mMetaData) ;
mMetaData = NS_STATIC_CAST(char*, nsMemory::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData) {
return NS_ERROR_OUT_OF_MEMORY ;
}
memcpy(mMetaData, data, length) ;
// Generate mInfo
nsresult rv = GenInfo() ;
if(NS_FAILED(rv))
return rv ;
// write through into mDB
rv = mDB->Put(mRecordID, mInfo, mInfoSize) ;
return rv ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetStoredContentLength(PRUint32 *aStoredContentLength)
{
PRInt64 fileSize;
nsresult rv = mFile->GetFileSize( &fileSize);
if (NS_SUCCEEDED(rv))
LL_L2UI( *aStoredContentLength, fileSize );
#ifdef DEBUG_dp
// Assert if our mFile is out of sync with the disk file
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIFile> spec;
PRInt64 realFileSize;
nsresult res = mFile->Clone(getter_AddRefs(spec));
if (NS_SUCCEEDED(res))
{
spec->GetFileSize(&realFileSize);
NS_ASSERTION(LL_EQ(fileSize, realFileSize), "Cache record file size doesn't match disk file size.");
}
}
#endif /* DEBUG_dp */
return rv;
}
// untill nsIFileSpec::Truncate() is in, we have to do all this ugly stuff
NS_IMETHODIMP
nsDiskCacheRecord::SetStoredContentLength(PRUint32 aStoredContentLength)
{
PRUint32 len = 0 ;
PRInt64 fileSize;
nsresult rv = mFile->GetFileSize( &fileSize) ;
if(NS_FAILED(rv))
return rv ;
LL_L2UI( len, fileSize );
if(len < aStoredContentLength)
{
NS_ERROR("Error: can not set filesize to something bigger than itself.\n") ;
return NS_ERROR_FAILURE ;
}
if(len > aStoredContentLength)
{
PRInt64 size;
LL_UI2L( size, aStoredContentLength );
rv = mFile->SetFileSize( size ) ;
if(NS_FAILED(rv))
return rv ;
// We just changed the file. Update our fileinfo.
UpdateFileInfo();
mDiskCache->mStorageInUse -= (len - aStoredContentLength) ;
}
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetSecurityInfo (nsISupports ** o_SecurityInfo)
{
NS_ENSURE_ARG(o_SecurityInfo);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecord::SetSecurityInfo (nsISupports * o_SecurityInfo)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecord::Delete(void)
{
if(mNumTransports)
return NS_ERROR_NOT_AVAILABLE ;
// Delete any file associated with this entry. Remember we might
// not created the file yet.
PRUint32 len = 0 ;
PRInt64 fileSize;
nsresult rv = mFile->GetFileSize( &fileSize) ;
if (NS_SUCCEEDED(rv))
{
mFile->Delete(PR_TRUE) ;
// updata the storage size
LL_L2UI(len, fileSize);
mDiskCache->mStorageInUse -= len ;
}
rv = mDB->Del(mRecordID, mKey, mKeyLength) ;
if (NS_FAILED(rv))
return NS_ERROR_FAILURE ;
else
{
mDiskCache->mNumEntries--;
return NS_OK ;
}
}
NS_IMETHODIMP
nsDiskCacheRecord::WriteComplete()
{
// Need to update the mFile we hold as the mFile might hold
// a stat cache that may be invalid now.
return UpdateFileInfo();
}
NS_IMETHODIMP
nsDiskCacheRecord::GetFile(nsIFile * *aFile)
{
if(!aFile)
return NS_ERROR_NULL_POINTER ;
*aFile = mFile ;
NS_ADDREF(*aFile) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::NewTransport(nsILoadGroup *loadGroup, nsITransport **_retval)
{
nsDiskCacheRecordTransport* transport =
new nsDiskCacheRecordTransport(this, loadGroup);
if(!transport)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = transport->Init();
if(NS_FAILED(rv))
return rv;
NS_ADDREF(transport);
*_retval = NS_STATIC_CAST(nsITransport*, transport);
return NS_OK;
}
//////////////////////////////////////////////////////////////////////////
// nsDiskCacheRecord methods
// file name is represented by a url string. I hope this would be more
// generic
nsresult
nsDiskCacheRecord::GenInfo()
{
if(mInfo)
nsMemory::Free(mInfo) ;
char* file_url=nsnull ;
PRUint32 name_len ;
mFile->GetPath(&file_url) ;
name_len = PL_strlen(file_url)+1 ;
mInfoSize = sizeof(PRUint32) ; // checksum for mInfoSize
mInfoSize += sizeof(PRInt32) ; // RecordID
mInfoSize += sizeof(PRUint32) ; // key length
mInfoSize += mKeyLength ; // key
mInfoSize += sizeof(PRUint32) ; // metadata length
mInfoSize += mMetaDataLength ; // metadata
mInfoSize += sizeof(PRUint32) ; // filename length
mInfoSize += name_len ; // filename
void* newInfo = nsMemory::Alloc(mInfoSize*sizeof(char)) ;
if(!newInfo) {
return NS_ERROR_OUT_OF_MEMORY ;
}
// copy the checksum mInfoSize
char* cur_ptr = NS_STATIC_CAST(char*, newInfo) ;
COPY_INT32(cur_ptr, &mInfoSize) ;
cur_ptr += sizeof(PRUint32) ;
// copy RecordID
COPY_INT32(cur_ptr, &mRecordID) ;
cur_ptr += sizeof(PRInt32) ;
// copy key length
COPY_INT32(cur_ptr, &mKeyLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy key
memcpy(cur_ptr, mKey, mKeyLength) ;
cur_ptr += mKeyLength ;
// copy metadata length
COPY_INT32(cur_ptr, &mMetaDataLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy metadata
memcpy(cur_ptr, mMetaData, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// copy file name length
COPY_INT32(cur_ptr, &name_len) ;
cur_ptr += sizeof(PRUint32) ;
// copy file name
memcpy(cur_ptr, file_url, name_len) ;
cur_ptr += name_len ;
nsMemory::Free(file_url);
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, newInfo) + mInfoSize);
mInfo = newInfo ;
return NS_OK ;
}
/*
* This Method suppose to get all the info from the db record
* and set them to accroding members. the original values
* will all be overwritten. only minimal error checking is performed.
*/
NS_IMETHODIMP
nsDiskCacheRecord::RetrieveInfo(void* aInfo, PRUint32 aInfoLength)
{
// reset everything
if(mInfo) {
nsMemory::Free(mInfo) ;
mInfo = nsnull ;
}
if(mKey) {
nsMemory::Free(mKey) ;
mKey = nsnull ;
}
if(mMetaData) {
nsMemory::Free(mMetaData) ;
mMetaData = nsnull ;
}
char * cur_ptr = NS_STATIC_CAST(char*, aInfo) ;
char* file_url ;
PRUint32 name_len ;
// set mInfoSize
COPY_INT32(&mInfoSize, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// check this at least
if(mInfoSize != aInfoLength)
return NS_ERROR_FAILURE ;
// set mRecordID
COPY_INT32(&mRecordID, cur_ptr) ;
cur_ptr += sizeof(PRInt32) ;
// set mKeyLength
COPY_INT32(&mKeyLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// poor man's attempt to detect corruption
if (mKeyLength > aInfoLength)
return NS_ERROR_FAILURE;
// set mKey
mKey = NS_STATIC_CAST(char*, nsMemory::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, cur_ptr, mKeyLength) ;
cur_ptr += mKeyLength ;
PRInt32 id ;
mDB->GetID(mKey, mKeyLength, &id) ;
// NS_ASSERTION(id==mRecordID, "\t ++++++ bad record, somethings wrong\n") ;
// bad record, somethings wrong
if (id != mRecordID) {
return NS_ERROR_FAILURE;
}
// set mMetaDataLength
COPY_INT32(&mMetaDataLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// poor man's attempt to detect corruption
if (mMetaDataLength > aInfoLength)
return NS_ERROR_FAILURE;
// set mMetaData
mMetaData = NS_STATIC_CAST(char*, nsMemory::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mMetaData, cur_ptr, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// get mFile name length
COPY_INT32(&name_len, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// poor man's attempt to detect corruption
if (name_len > aInfoLength)
return NS_ERROR_FAILURE;
// get mFile native name
file_url = NS_STATIC_CAST(char*, nsMemory::Alloc(name_len*sizeof(char))) ;
if(!file_url)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(file_url, cur_ptr, name_len) ;
cur_ptr += name_len ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, aInfo) + mInfoSize);
// create mFile if Init() isn't called
if(!mFile.get() ) {
nsCOMPtr< nsILocalFile> file;
NS_NewLocalFile( file_url, PR_FALSE ,getter_AddRefs(file));
mFile = file;
if(!mFile.get())
return NS_ERROR_OUT_OF_MEMORY ;
}
else
{
// setup mFile
nsCOMPtr<nsILocalFile> file;
nsresult rv;
file = do_QueryInterface( mFile, &rv ) ;
if ( NS_SUCCEEDED( rv ) )
file->InitWithPath(file_url) ;
}
nsMemory::Free(file_url);
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::UpdateFileInfo()
{
// The mFile we store might be out of date. Atlease
// we know that our implementation of nsIFile stores
// stat data in a cache. We need to invalidate that cache
// at critical points or atleast when we know we changed
// file info.
// nsIFile::Sync(); -dp- need to get this into the api
// We fake it now with Clone. All email dougt@netscape.com
// to fix it. Please....
nsCOMPtr<nsIFile> spec;
nsresult rv = mFile->Clone(getter_AddRefs(spec));
if (NS_SUCCEEDED(rv))
mFile = spec;
return rv;
}